Reverse Proxy Setup for Self-Hosted Supabase: Nginx, Traefik, and Caddy

Complete guide to configuring Nginx, Traefik, or Caddy as a reverse proxy for self-hosted Supabase with SSL and production best practices.

Cover Image for Reverse Proxy Setup for Self-Hosted Supabase: Nginx, Traefik, and Caddy

If you've deployed Supabase on your own server, you've probably noticed something: the default setup exposes services on multiple ports without SSL. That's fine for local development, but for production, you need a proper reverse proxy handling SSL termination, routing, and security.

This guide walks through setting up three popular reverse proxies—Nginx, Traefik, and Caddy—for self-hosted Supabase. We'll cover the trade-offs of each so you can choose what fits your stack.

Why You Need a Reverse Proxy

Self-hosted Supabase runs multiple services: Kong (API gateway on port 8000), Studio (dashboard on port 3000), and various internal services. Without a reverse proxy, you'd need to:

  • Manage SSL certificates for each service individually
  • Expose multiple ports to the internet
  • Handle routing and load balancing manually
  • Configure security headers per-service

A reverse proxy sits in front of everything, providing a single entry point that handles SSL termination, routes traffic to the correct backend service, and enforces security policies consistently.

The community consensus is clear: use an external reverse proxy (Nginx, Caddy, or Traefik) in front of Kong to handle SSL termination, then forward to Kong's HTTP port 8000. This separation of concerns makes certificate management easier and keeps your Supabase configuration cleaner.

Choosing Your Reverse Proxy

Each option has distinct strengths:

FeatureNginxTraefikCaddy
ConfigurationFile-based, verboseLabels or file, dynamicCaddyfile, minimal
SSL AutomationManual or CertbotBuilt-in Let's EncryptBuilt-in, automatic
Docker IntegrationRequires manual configNative Docker labelsGood, less automatic
Learning CurveModerateSteeperGentle
Resource UsageLightModerateLight
DashboardNone built-inYes, real-timeNone built-in

Nginx is the battle-tested choice. If your team already knows Nginx, stick with it. The configuration is verbose but well-documented.

Traefik shines in containerized environments. Its Docker label-based configuration means services can self-register their routes. The dynamic dashboard helps with debugging.

Caddy wins on simplicity. Automatic HTTPS with zero configuration makes it ideal for smaller deployments or teams who don't want to manage certificate renewal.

Nginx Configuration

Nginx remains the most widely deployed reverse proxy. Here's a production-ready configuration for Supabase:

upstream supabase_studio {
    server 127.0.0.1:3000;
}

upstream supabase_kong {
    server 127.0.0.1:8000;
}

server {
    listen 80;
    server_name supabase.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name supabase.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/supabase.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/supabase.yourdomain.com/privkey.pem;
    
    # Modern SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # REST API
    location /rest/v1/ {
        proxy_pass http://supabase_kong;
        proxy_set_header Host $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;
    }

    # Authentication
    location /auth/v1/ {
        proxy_pass http://supabase_kong;
        proxy_set_header Host $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;
    }

    # Realtime - requires WebSocket support
    location /realtime/v1/ {
        proxy_pass http://supabase_kong;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 86400;
    }

    # Storage
    location /storage/v1/ {
        proxy_pass http://supabase_kong;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        client_max_body_size 100M;
    }

    # Studio Dashboard
    location / {
        proxy_pass http://supabase_studio;
        proxy_set_header Host $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;
    }
}

For SSL certificates, use Certbot:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d supabase.yourdomain.com

Certbot will automatically update your Nginx config and set up renewal cron jobs.

Traefik Configuration

Traefik offers dynamic configuration through Docker labels, which suits containerized Supabase deployments well. The community has developed several templates specifically for this use case.

First, create a docker-compose.override.yml to add Traefik labels to your Supabase services:

version: "3.8"

services:
  traefik:
    image: traefik:v3.0
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "[email protected]"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.yourdomain.com`)"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.middlewares=auth"
      - "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$..."

  kong:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.supabase-api.rule=Host(`supabase.yourdomain.com`) && PathPrefix(`/rest/v1`, `/auth/v1`, `/storage/v1`, `/realtime/v1`)"
      - "traefik.http.routers.supabase-api.entrypoints=websecure"
      - "traefik.http.routers.supabase-api.tls.certresolver=letsencrypt"
      - "traefik.http.services.supabase-api.loadbalancer.server.port=8000"

  studio:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.supabase-studio.rule=Host(`supabase.yourdomain.com`)"
      - "traefik.http.routers.supabase-studio.entrypoints=websecure"
      - "traefik.http.routers.supabase-studio.tls.certresolver=letsencrypt"
      - "traefik.http.services.supabase-studio.loadbalancer.server.port=3000"

Key advantages of Traefik for Supabase:

  • Dynamic dashboard for real-time monitoring of routes and services
  • Automatic service discovery when you add new projects
  • Middleware support for rate limiting and access controls
  • Native Docker integration means less configuration drift

One thing to watch: while Traefik handles incoming traffic on ports 80 and 443, the Supabase services remain accessible on their native ports. Configure your firewall to block direct access:

# Allow only HTTP/HTTPS through Traefik
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 3000/tcp
sudo ufw deny 8000/tcp

Caddy Configuration

Caddy takes a "just works" approach to reverse proxying. Automatic HTTPS is enabled by default—no certificate configuration required.

Create a Caddyfile:

supabase.yourdomain.com {
    # REST API
    handle /rest/v1/* {
        reverse_proxy localhost:8000
    }

    # Authentication
    handle /auth/v1/* {
        reverse_proxy localhost:8000
    }

    # Realtime with WebSocket support
    handle /realtime/v1/* {
        reverse_proxy localhost:8000
    }

    # Storage with increased body size
    handle /storage/v1/* {
        reverse_proxy localhost:8000
        request_body {
            max_size 100MB
        }
    }

    # Studio Dashboard (catch-all)
    handle {
        reverse_proxy localhost:3000
    }

    # Security headers
    header {
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        X-XSS-Protection "1; mode=block"
        -Server
    }
}

Run Caddy:

# Using Docker
docker run -d \
  --name caddy \
  --network host \
  -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
  -v caddy_data:/data \
  caddy:latest

# Or install directly
sudo apt install caddy
sudo systemctl start caddy

Caddy automatically obtains and renews Let's Encrypt certificates. The entire SSL configuration is implicit in using a domain name—no manual steps required.

Production Security Considerations

Whichever proxy you choose, apply these security practices:

Firewall Configuration

Only expose necessary ports. Supabase internal services should never be directly accessible:

# Example with UFW
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Rate Limiting

Protect against abuse by adding rate limits. In Nginx:

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

location /rest/v1/ {
    limit_req zone=api burst=20 nodelay;
    # ... rest of config
}

Studio Access Control

The Supabase Studio dashboard should not be publicly accessible without authentication. Consider:

  1. IP whitelisting for known admin IPs
  2. Basic auth at the proxy level
  3. VPN access only for sensitive environments

With Nginx:

location / {
    allow 192.168.1.0/24;  # Your office/VPN range
    deny all;
    proxy_pass http://supabase_studio;
}

Common Pitfalls and Fixes

WebSocket connections failing: Realtime features require WebSocket support. Ensure your proxy passes the Upgrade and Connection headers correctly. The Nginx config above includes these.

Large file uploads timing out: Increase proxy timeouts and client body size limits for storage operations.

Mixed content warnings: Ensure X-Forwarded-Proto is set correctly so Supabase generates HTTPS URLs.

Certificate renewal failures: Let's Encrypt's HTTP-01 challenge requires port 80 to be accessible. Don't block it entirely—redirect to HTTPS instead.

Simplifying with Supascale

Configuring reverse proxies, managing SSL certificates, and securing access are the kind of operational tasks that distract from building your product. Supascale handles this automatically—custom domains with free SSL certificates are configured through a simple UI, no manual proxy setup required.

Beyond domains, Supascale provides automated backups to S3-compatible storage, OAuth provider configuration, and selective service deployment. At $39.99 one-time for unlimited projects, it eliminates the operational overhead while you retain full control of your data.

For teams who want to manage their own proxy layer, Supascale works alongside your existing infrastructure—you're not locked into any particular setup.

Conclusion

A proper reverse proxy transforms self-hosted Supabase from a development experiment into a production-ready platform. Your choice between Nginx, Traefik, and Caddy depends on your team's existing expertise and infrastructure preferences:

  • Nginx for teams with existing Nginx knowledge who want maximum control
  • Traefik for containerized environments with dynamic service discovery needs
  • Caddy for the simplest possible setup with automatic SSL

All three can secure a Supabase deployment effectively. The key is ensuring consistent configuration across all service routes, proper WebSocket support for Realtime, and locked-down firewall rules.

If you'd rather skip the proxy configuration entirely, check out Supascale's approach to custom domains and SSL automation.


Further Reading