Many people get MinIO running on their server and then get stuck. The storage part works fine on port 9000, but the moment you try to run MinIO behind a HTTPS reverse proxy with a real domain and a valid SSL certificate, things break. The console redirects to the wrong port, the S3 API returns errors, or the browser shows a certificate warning.
This guide will show you how to get MinIO live behind Nginx or Caddy with TLS and a custom domain.
What You Need to Run MinIO Behind a HTTPS Reverse Proxy
First of all, you must set up MinIO. To do this, you can follow the MinIO installation guide to get your MinIO instance running, then follow these steps to add the proxy layer.
To run MinIO behind a HTTPS reverse proxy, you need:
- A server with MinIO running, API on port 9000, and Console on port 9001.
- Nginx or Caddy is installed on the same server.
- A domain name with DNS access.
- Ports 80 and 443 are open in your firewall.
If you need a reliable server, you can run storage-heavy self-hosted stacks on a Dedicated Server with fast NVMe from PerLod, which handles the I/O load MinIO needs without bottlenecks.
Plan Your Subdomains and Point Your DNS Records
When you run MinIO behind a HTTPS reverse proxy, MinIO uses two separate ports internally:
| Port |
Purpose |
| 9000 |
S3 API (used by apps, AWS CLI, mc client) |
| 9001 |
Web Console (the browser UI) |
The best way is to give each one its own subdomain:
s3.yourdomain.com: proxies to port 9000 (API)
console.yourdomain.com: proxies to port 9001 (Console)
This will avoid redirect loops and keep your S3 API clean from browser traffic.
Then, log in to your DNS provider, wherever you registered your domain, and add two A records:
1s3.yourdomain.com A YOUR_SERVER_IP2console.yourdomain.com A YOUR_SERVER_IP
Wait for propagation; DNS changes can take a few minutes to up to an hour. You can check with:
dig s3.yourdomain.com +shortdig console.yourdomain.com +short
Both should return your server's IP before you continue.
If you need a domain, you can easily get a domain from PerLod and connect it to your server using the A records above.
Set MinIO Environment Variables
When you run MinIO behind a HTTPS reverse proxy, MinIO needs to know its own public URLs. Without these, the console redirects to the internal port (9001) instead of your domain, which breaks the browser UI.
Edit your MinIO environment file. If you installed via the system package, use this command:
sudo nano /etc/default/minio
If you are using Docker, add these to your docker-compose.yml under environment:.
Add these two variables:
MINIO_SERVER_URL=https://s3.yourdomain.comMINIO_BROWSER_REDIRECT_URL=https://console.yourdomain.com
- MINIO_SERVER_URL: tells MinIO what its public API URL is.
- MINIO_BROWSER_REDIRECT_URL: tells the console where to redirect the browser.
Then restart MinIO:
sudo systemctl restart minio docker compose restart minio
Now you can use one of the following options to run MinIO behind a HTTPS reverse proxy with Nginx or Caddy.
Option A: Run MinIO Behind Nginx Reverse Proxy
To run MinIO behind a HTTPS reverse proxy using Nginx, you must install Nginx and Certbot on your server:
sudo apt updatesudo apt install nginx certbot python3-certbot-nginx -y
Create a new Nginx config file for MinIO:
sudo nano /etc/nginx/conf.d/minio.conf
Paste the following configuration with your domains to run MinIO behind a HTTPS reverse proxy using Nginx:
server { listen 80; server_name s3.yourdomain.com; return 301 https://$host$request_uri;} server { listen 443 ssl http2; server_name s3.yourdomain.com; ssl_certificate /etc/letsencrypt/live/s3.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/s3.yourdomain.com/privkey.pem; ignore_invalid_headers off; client_max_body_size 0; proxy_buffering off; proxy_request_buffering off; location / { proxy_pass http://127.0.0.1:9000; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Connection ""; chunked_transfer_encoding off; proxy_connect_timeout 300; proxy_read_timeout 300; proxy_send_timeout 300; }} server { listen 80; server_name console.yourdomain.com; return 301 https://$host$request_uri;} server { listen 443 ssl http2; server_name console.yourdomain.com; ssl_certificate /etc/letsencrypt/live/console.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/console.yourdomain.com/privkey.pem; ignore_invalid_headers off; client_max_body_size 0; proxy_buffering off; proxy_request_buffering off; location / { proxy_pass http://127.0.0.1:9001; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_connect_timeout 300; proxy_read_timeout 300; proxy_send_timeout 300; }}
Save and close the file. Then, get TLS certificates with Certbot:
sudo certbot --nginx -d s3.yourdomain.comsudo certbot --nginx -d console.yourdomain.com
Certbot will automatically edit the Nginx config to point to the certificate files.
Finally, test and reload Nginx:
sudo nginx -tsudo systemctl reload nginx
For more detailed information, you can visit the MinIO Official Nginx Proxy Integration Docs.
Option B: Run MinIO Behind Caddy Reverse Proxy
Caddy is simpler if you want automatic TLS without running Certbot. When you run MinIO behind a HTTPS reverse proxy using Caddy, it handles Let's Encrypt certificates automatically.
Use the commands below to install Caddy:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curlcurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpgcurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.listsudo apt updatesudo apt install caddy -y
sudo nano /etc/caddy/Caddyfile
Add the following configuration with your domains to run MinIO behind a HTTPS reverse proxy using Caddy:
s3.yourdomain.com { reverse_proxy localhost:9000 { header_up Host {host} header_up X-Real-IP {remote_host} header_up X-Forwarded-Proto {scheme} }} console.yourdomain.com { reverse_proxy localhost:9001 { header_up Host {host} header_up X-Real-IP {remote_host} header_up X-Forwarded-Proto {scheme} }}
Caddy automatically obtains and renews TLS certificates from Let's Encrypt when you use a real domain name.
Once you are done, start and enable Caddy:
sudo systemctl enable caddysudo systemctl restart caddysudo systemctl status caddy
Verify with the MinIO mc Client
After the proxy is live, confirm everything is working using the MinIO mc client.
Install mc with:
curl -O https://dl.min.io/client/mc/release/linux-amd64/mcchmod +x mcsudo mv mc /usr/local/bin/
Set an alias pointing to your new HTTPS domain:
mc alias set myminio https://s3.yourdomain.com YOUR_ACCESS_KEY YOUR_SECRET_KEY
List buckets with the command below. If this works, the proxy is correct:
mc mb myminio/test-bucket
echo "proxy works" > test.txtmc cp test.txt myminio/test-bucket/
mc ls myminio/test-bucket
If you use the AWS CLI with your MinIO setup, you can verify with:
aws --endpoint-url https://s3.yourdomain.com s3 lsaws --endpoint-url https://s3.yourdomain.com s3 mb s3://test-bucketaws --endpoint-url https://s3.yourdomain.com s3 cp test.txt s3://test-bucket/
Both commands should succeed without any SSL errors if Certbot or Caddy issued a valid certificate. If you see SSL errors, check that the certificate covers both subdomains.
Conclusion
In this guide, you learned how to run MinIO behind a HTTPS reverse proxy. By setting the right MinIO environment variables and using a simple Nginx or Caddy reverse proxy, you avoid broken redirects, SSL issues, and S3 errors. After this setup, your apps and S3 clients can communicate with MinIO over a stable, secure endpoint ready for real production use.