How to Run MinIO Behind Nginx or Caddy with TLS and a Custom Domain

Updated on Aug 18, 2026
Kimberly N
6 MINS READ
Table of Contents
run MinIO behind a HTTPS reverse proxy

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:

HTML/XML
s3.yourdomain.com       A   YOUR_SERVER_IPconsole.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:

Bash
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:

Bash
sudo nano /etc/default/minio

If you are using Docker, add these to your docker-compose.yml under environment:.

Add these two variables:

Bash
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:

Bash
# System install:sudo systemctl restart minio # Docker: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:

Bash
sudo apt updatesudo apt install nginx certbot python3-certbot-nginx -y

Create a new Nginx config file for MinIO:

Bash
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:

Bash
# --- MinIO S3 API ---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;     # Required for MinIO — allow special characters in headers    ignore_invalid_headers off;     # Allow large file uploads — 0 means unlimited    client_max_body_size 0;     # Disable Nginx buffering so MinIO streams directly    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 "";         # Required for S3 multipart uploads        chunked_transfer_encoding off;         # Increase timeouts for large file operations        proxy_connect_timeout 300;        proxy_read_timeout    300;        proxy_send_timeout    300;    }} # --- MinIO Web Console ---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;         # Required for WebSocket (console uses it)        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:

Bash
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:

Bash
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:

Bash
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

Create the Caddyfile:

Bash
 sudo nano /etc/caddy/Caddyfile

Add the following configuration with your domains to run MinIO behind a HTTPS reverse proxy using Caddy:

Bash
# MinIO S3 APIs3.yourdomain.com {    reverse_proxy localhost:9000 {        header_up Host {host}        header_up X-Real-IP {remote_host}        header_up X-Forwarded-Proto {scheme}    }} # MinIO Web Consoleconsole.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:

Bash
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:

Bash
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:

Bash
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:

Bash
mc ls myminio

Create a test bucket:

Bash
mc mb myminio/test-bucket

Upload a test file:

Bash
echo "proxy works" > test.txtmc cp test.txt myminio/test-bucket/

Verify the file is there

Bash
mc ls myminio/test-bucket

If you use the AWS CLI with your MinIO setup, you can verify with:

Bash
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.

No, you can use a single domain and proxy the Console under a /minio/ui/ path, but using two subdomains is much simpler and avoids path-rewrite issues.

Yes, but S3 clients will reject it unless you add --no-verify-ssl (AWS CLI) or --insecure (mc). Use Let's Encrypt for production.

Without it, Nginx buffers the entire file to disk before sending it to MinIO. For large uploads, this causes timeouts and slow transfers.