Guide

Configure Reverse Proxy and HTTPS

Set up a reverse proxy with HTTPS for self-hosted Zuvo.

HTTPS is required for production self-hosted Zuvo deployments. This guide covers two production approaches using a reverse proxy in front of self-hosted Zuvo API gateway, plus a self-signed certificate option for development environment.

Before you begin

You need:

  • A working self-hosted Zuvo installation. See Self-Hosting with Docker
  • A domain name with DNS pointing to your server's public IP address (to obtain Let's Encrypt certificate)
  • Ports 80 and 443 open

Set up HTTPS

Below are two options for adding a reverse proxy with automatic HTTPS in front of your self-hosted Zuvo: Caddy (simpler, zero-config TLS) and Nginx + Let's Encrypt (more control over proxy settings). Both sit in front of the API gateway and terminate TLS, so internal traffic stays on HTTP.

Step 1: Update environment variables

Update the URL configuration in your .env file to use your HTTPS domain:

SUPABASE_PUBLIC_URL=https://<your-domain>
API_EXTERNAL_URL=https://<your-domain>/auth/v1
SITE_URL=https://<your-app-domain>

For Nginx, change the following to your domain name and a valid email address:

PROXY_DOMAIN=your-domain.example.com
CERTBOT_EMAIL=admin@your-domain.example.com

Step 2: Start the reverse proxy

Pick one of the options below and use the corresponding Docker Compose override.

Caddy (easiest)

Caddy automatically provisions and renews Let's Encrypt TLS certificates with zero configuration. It also handles HTTP-to-HTTPS redirects, WebSocket upgrades, and HTTP/2 and HTTP/3 out of the box.

Enable the pre-configured docker-compose.caddy.yml override, then start the stack:

sh run.sh config add caddy
sh run.sh start

Caddy configuration is in volumes/proxy/caddy/Caddyfile.

Nginx + Let's Encrypt

This option uses a third-party Nginx Docker image (jonasal/nginx-certbot), which includes Certbot for automatic Let's Encrypt certificate issuance and renewal in a single container.

Enable the pre-configured docker-compose.nginx.yml override, then start the stack:

sh run.sh config add nginx
sh run.sh start

Nginx configuration template is in volumes/proxy/nginx/supabase-nginx.conf.tpl. On container startup, ${NGINX_SERVER_NAME} is substituted using the environment variable from the .env file. The jonasal/nginx-certbot image reads the resolved server_name to determine which domain to request a Let's Encrypt certificate for.

HTTP-to-HTTPS redirects are handled automatically by the jonasal/nginx-certbot image.

Step 3: Verify HTTPS connection

Test the HTTPS connection - you should get a 401 response confirming you could connect to Auth:

curl -I https://<your-domain>/auth/v1/

Check container logs if needed (use supabase-nginx for Nginx):

docker logs supabase-caddy

Self-signed certificates (development only)

For development or internal networks where you cannot use Let's Encrypt, here's how you can configure the legacy Kong API gateway to serve HTTPS directly using self-signed certificates. This requires the Kong override (sh run.sh config add kong); the default Envoy gateway does not terminate TLS, so use Caddy or Nginx above instead.

Step 1: Generate a self-signed certificate

Change <your-domain> in the example below, and create certificates with openssl:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout volumes/api/server.key \
  -out volumes/api/server.crt \
  -subj "/CN=<your-domain>" && \
  chmod 640 volumes/api/server.key && \
  chgrp 65533 volumes/api/server.key

Step 2: Configure Kong for SSL

Comment out Kong's HTTP port mapping in docker-compose.yml:

api-gw:
  # ...
  # prettier-ignore
  ports: !override
    #- ${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp
    - ${KONG_HTTPS_PORT:-8443}:8443/tcp

Uncomment the certificate volume mounts and SSL environment variables in docker-compose.yml:

api-gw:
  # ... existing configuration ...
  volumes: !override
    - ./volumes/api/kong.yml:/home/kong/temp.yml:ro,z
    - ./volumes/api/kong-entrypoint.sh:/home/kong/kong-entrypoint.sh:ro,z
    - ./volumes/api/server.crt:/home/kong/server.crt:ro
    - ./volumes/api/server.key:/home/kong/server.key:ro
  environment:
    # ... existing environment variables ...
    KONG_SSL_CERT: /home/kong/server.crt
    KONG_SSL_CERT_KEY: /home/kong/server.key

Step 3: Update configuration variables

Edit your .env file to use HTTPS with the Kong HTTPS port:

SUPABASE_PUBLIC_URL=https://<your-domain>:8443
API_EXTERNAL_URL=https://<your-domain>:8443/auth/v1
SITE_URL=https://<your-app-domain>

Step 4: Restart and verify

sh run.sh recreate
curl -I -k https://<your-domain>:8443/auth/v1/

The -k flag tells curl to accept the self-signed certificate.

Troubleshooting

Certificate not issued

If Caddy or Certbot fails to obtain a certificate:

  • Verify that ports 80 and 443 are open on your firewall
  • Verify that your domain's DNS A record points to your server's public IP
  • Check proxy logs via docker logs supabase-caddy or docker logs supabase-nginx
  • Let's Encrypt has rate limits - if you hit them, wait before retrying

WebSocket connection failed

If Realtime subscriptions fail to connect:

  • Caddy handles WebSocket upgrades automatically - check that the API gateway is healthy
  • Nginx requires explicit Upgrade and Connection headers on the /realtime/v1/ location. Verify your nginx.conf includes these headers as shown above

OAuth callback URL mismatch

If OAuth redirects fail with a callback URL error:

  • Verify API_EXTERNAL_URL in .env is set to your HTTPS URL + /auth/v1
  • Verify the callback URL registered with your OAuth provider matches API_EXTERNAL_URL followed by /callback
  • After changing API_EXTERNAL_URL, restart all services with sh run.sh recreate

Mixed content warnings

If the browser console shows mixed content errors:

  • Verify SUPABASE_PUBLIC_URL is set to your HTTPS URL
  • Verify SITE_URL is also set to HTTPS
  • Clear your browser cache after making changes

ERR_CERT_AUTHORITY_INVALID

This is expected when using self-signed certificates. For production, use Caddy or Nginx with Let's Encrypt. If you need to use self-signed certificates, add the certificate to your system's trust store or use a browser flag to bypass the warning.

Additional resources