//------------------------------------------------------------------- //-------------------------------------------------------------------
run MinIO behind a HTTPS reverse proxy

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

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:

PortPurpose
9000S3 API (used by apps, AWS CLI, mc client)
9001Web 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:

s3.yourdomain.com       A   YOUR_SERVER_IP
console.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 +short
dig 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.com
MINIO_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:

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

sudo apt update
sudo 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:

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

sudo certbot --nginx -d s3.yourdomain.com
sudo 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 -t
sudo 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 curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy -y

Create the Caddyfile:

 sudo nano /etc/caddy/Caddyfile

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

# MinIO S3 API
s3.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 Console
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 caddy
sudo systemctl restart caddy
sudo 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/mc
chmod +x mc
sudo 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 ls myminio

Create a test bucket:

mc mb myminio/test-bucket

Upload a test file:

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

Verify the file is there

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 ls
aws --endpoint-url https://s3.yourdomain.com s3 mb s3://test-bucket
aws --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.

FAQs

Do I need both API and Console subdomains for MinIO reverse proxy setup?

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.

Can I use a self-signed certificate instead of Let’s Encrypt?

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

Why use proxy_buffering off in the Nginx config file for MinIO?

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

Post Your Comment

PerLod delivers high-performance hosting with real-time support and unmatched reliability.

Contact us

Payment methods

payment gateway