MinIO behind a reverse proxy: API, console and presigned URLs
- Use separate hostnames
- Pass the Host header unchanged
- Tell MinIO its public URLs
- Presigned URLs from your app
- Large uploads
- Lock down the console
- Check it
MinIO serves two things on two ports: the S3 API, usually on 9000, and the web console, usually on 9001. Putting both behind a reverse proxy with HTTPS is straightforward, but a few details decide whether S3 clients and presigned URLs work.
Use separate hostnames
Give the API and the console their own hostnames, such as s3.example.com and minio.example.com. Serving the S3 API under a path prefix like /s3/ breaks request signatures, because clients sign the path they send and MinIO verifies the path it receives.
With Caddy:
s3.example.com {
reverse_proxy minio:9000
}
minio.example.com {
reverse_proxy minio:9001
}
Pass the Host header unchanged
S3 signatures include the Host header. If the proxy rewrites it to minio:9000, every signed request fails with SignatureDoesNotMatch. Caddy and Traefik keep the original host by default. With Nginx, set proxy_set_header Host $http_host;.
Tell MinIO its public URLs
Set the public addresses so MinIO generates correct links:
environment:
MINIO_SERVER_URL: https://s3.example.com
MINIO_BROWSER_REDIRECT_URL: https://minio.example.com
MINIO_SERVER_URL matters for presigned URLs created from the console and for console operations that call the API. Without it, links may point to an internal address.
Presigned URLs from your app
Your application must create presigned URLs using the public endpoint, not the internal Docker name. A URL signed for http://minio:9000 will not work in a browser, and you cannot fix it by rewriting the hostname afterwards, because the host is part of the signature. Configure the SDK client with https://s3.example.com and path-style addressing.
Large uploads
Remove or raise body size limits in the proxy. Nginx defaults to 1 MB with client_max_body_size. Disable request buffering for uploads so large files stream through instead of filling the proxy’s disk. Multipart uploads from SDKs help too.
Lock down the console
The console is an admin interface. Consider restricting it by IP, putting it behind a VPN, or not exposing it publicly at all and reaching it through an SSH tunnel. The S3 API can stay public with per-bucket access keys.
Check it
mc alias set prod https://s3.example.com ACCESS SECRET
mc ls prod
mc share download prod/bucket/file.txt
Open the shared link in a browser. If it downloads, signatures and public URLs are right.
For installation, storage layout and caveats, see this guide to self-hosting MinIO object storage.
Disclosure: I help build Peon.
Write a comment