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:
| Feature | Nginx | Traefik | Caddy |
|---|---|---|---|
| Configuration | File-based, verbose | Labels or file, dynamic | Caddyfile, minimal |
| SSL Automation | Manual or Certbot | Built-in Let's Encrypt | Built-in, automatic |
| Docker Integration | Requires manual config | Native Docker labels | Good, less automatic |
| Learning Curve | Moderate | Steeper | Gentle |
| Resource Usage | Light | Moderate | Light |
| Dashboard | None built-in | Yes, real-time | None 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:
- IP whitelisting for known admin IPs
- Basic auth at the proxy level
- 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.
