From 001faf98b6c92f15db48418bd14a7cb4b7985e05 Mon Sep 17 00:00:00 2001 From: "Andrey A." <56412611+aantti@users.noreply.github.com> Date: Tue, 3 Mar 2026 18:09:46 +0100 Subject: [PATCH] docs: add https proxy how-to for self-hosted (#43293) ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? - Add a new how-to guide covering PR #43291 - Explain how to use an https proxy on top of [self-hosted Supabase](https://supabase.com/docs/guides/self-hosting) API gateway (Kong) --------- Co-authored-by: Chris Chinchilla --- .../NavigationMenu.constants.ts | 6 +- .../content/guides/self-hosting/docker.mdx | 6 + .../self-hosting/self-hosted-proxy-https.mdx | 228 ++++++++++++++++++ docker/.env.example | 2 +- docker/docker-compose.yml | 4 + 5 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 apps/docs/content/guides/self-hosting/self-hosted-proxy-https.mdx diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 42c83651762..0ca56c33284 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -2850,6 +2850,10 @@ export const self_hosting: NavMenuConstant = { name: 'How-to Guides', items: [ { name: 'Self-Hosted Functions', url: '/guides/self-hosting/self-hosted-functions' }, + { + name: 'Add Reverse Proxy with HTTPS', + url: '/guides/self-hosting/self-hosted-proxy-https', + }, { name: 'Restore Project from Platform', url: '/guides/self-hosting/restore-from-platform', @@ -2858,7 +2862,7 @@ export const self_hosting: NavMenuConstant = { { name: 'Copy Storage from Platform', url: '/guides/self-hosting/copy-from-platform-s3' }, { name: 'Configure Social Login (OAuth)', url: '/guides/self-hosting/self-hosted-oauth' }, { name: 'Configure Phone Login & MFA', url: '/guides/self-hosting/self-hosted-phone-mfa' }, - { name: 'Enabling MCP server', url: '/guides/self-hosting/enable-mcp' }, + { name: 'Enable MCP server', url: '/guides/self-hosting/enable-mcp' }, ], }, { diff --git a/apps/docs/content/guides/self-hosting/docker.mdx b/apps/docs/content/guides/self-hosting/docker.mdx index 74f9564ca3e..a196650d77b 100644 --- a/apps/docs/content/guides/self-hosting/docker.mdx +++ b/apps/docs/content/guides/self-hosting/docker.mdx @@ -428,6 +428,12 @@ By default all files are stored locally on the server. You can connect Storage t See the [Configure S3 Storage](/docs/guides/self-hosting/self-hosted-s3) guide for detailed setup instructions. +#### Configuring HTTPS + +By default, Supabase is accessible over HTTP. For production deployments, especially when using OAuth providers, you need HTTPS with a valid TLS certificate. The recommended approach is to place a reverse proxy (such as Caddy or Nginx) in front of Kong. + +See the [Configure HTTPS](/docs/guides/self-hosting/self-hosted-proxy-https) guide for setup instructions. + #### Configuring social login (OAuth) providers See the [Configure Social Login (OAuth) Providers](/docs/guides/self-hosting/self-hosted-oauth) guide for setup instructions. diff --git a/apps/docs/content/guides/self-hosting/self-hosted-proxy-https.mdx b/apps/docs/content/guides/self-hosting/self-hosted-proxy-https.mdx new file mode 100644 index 00000000000..25723468ba8 --- /dev/null +++ b/apps/docs/content/guides/self-hosting/self-hosted-proxy-https.mdx @@ -0,0 +1,228 @@ +--- +title: 'Configure Reverse Proxy and HTTPS' +description: 'Set up a reverse proxy with HTTPS for self-hosted Supabase.' +subtitle: 'Set up a reverse proxy with HTTPS for self-hosted Supabase.' +--- + +HTTPS is required for production self-hosted Supabase deployments. This guide covers two production approaches using a reverse proxy in front of self-hosted Supabase API gateway, plus a self-signed certificate option for development environment. + +## Before you begin + +You need: + +- A working self-hosted Supabase installation. See [Self-Hosting with Docker](/docs/guides/self-hosting/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 Supabase: **Caddy** (simpler, zero-config TLS) and **Nginx + Let's Encrypt** (more control over proxy settings). Both sit in front of Kong and terminate TLS, so internal traffic stays on HTTP. + + + +If you already run [HAProxy](https://www.haproxy.com/), [Traefik](https://traefik.io/), [Nginx Proxy Manager](https://nginxproxymanager.com/), or another reverse proxy for your infrastructure, you can use it instead of Caddy or Nginx above. The key requirements are: + +- Proxy to Kong on port `8000` (or `:8000` if the proxy runs outside the Docker network) +- Enable WebSocket support (required for Realtime) +- Proxy traffic to Storage directly to the container, bypassing Kong +- Add `X-Forwarded` headers to all requests +- Comment out Kong's host port bindings in `docker-compose.yml` if the proxy runs in the same Docker network +- Update `SUPABASE_PUBLIC_URL`, `API_EXTERNAL_URL`, and `SITE_URL` in `.env` to your HTTPS URL + + + +### Step 1: Remove public port bindings for API gateway + +Comment out Kong's host port mappings in `docker-compose.yml` so that it's not exposed to the Internet: + +```yaml +kong: + # ... + ports: + # - ${KONG_HTTP_PORT}:8000/tcp + # - ${KONG_HTTPS_PORT}:8443/tcp +``` + +Kong remains accessible to other containers on the internal Docker network. + +### Step 2: Update environment variables + +Update the URL configuration in your `.env` file to use your HTTPS domain: + +``` +SUPABASE_PUBLIC_URL=https:// +API_EXTERNAL_URL=https:// +SITE_URL=https:// +``` + +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 3: Start the reverse proxy + +Pick one of the options below and use the corresponding Docker Compose overlay. + + + +[Caddy](https://caddyserver.com/) 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. + +Start Caddy by using the pre-configured `docker-compose.caddy.yml` overlay: + +```sh +docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d +``` + +Caddy configuration is in `volumes/proxy/caddy/Caddyfile`. + + + + +This option uses a 3rd party Nginx Docker image ([`jonasal/nginx-certbot`](https://github.com/JonasAlfredsson/docker-nginx-certbot)), which includes Certbot for automatic Let's Encrypt certificate issuance and renewal in a single container. + +Start Nginx by using the pre-configured `docker-compose.nginx.yml` overlay: + +```sh +docker compose -f docker-compose.yml -f docker-compose.nginx.yml up -d +``` + +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`](https://github.com/JonasAlfredsson/docker-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 4: Verify HTTPS connection + +```sh +curl -I https:///auth/v1/ +``` + +You should receive a `401` response confirming you could connect to Auth. + +## Self-signed certificates (development only) + + + +Self-signed certificates trigger browser warnings and are rejected by most OAuth providers. Use this approach only in development environment or internal networks. + + + +For development or internal networks where you cannot use Let's Encrypt, you can configure Kong to serve HTTPS directly using self-signed certificates. + +### Step 1: Generate a self-signed certificate + +Change `` in the example below, and create certificates with `openssl`: + +```sh +openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout volumes/api/server.key \ + -out volumes/api/server.crt \ + -subj "/CN=" && \ + chmod 640 volumes/api/server.key && \ + chgrp 65533 volumes/api/server.key +``` + +{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */} + +### Step 2: Configure Kong for SSL + +Comment out Kong's HTTP port mapping in `docker-compose.yml`: + +```yaml +kong: + # ... + ports: + # - ${KONG_HTTP_PORT}:8000/tcp +``` + +Uncomment the certificate volume mounts and SSL environment variables in `docker-compose.yml`: + +```yaml +kong: + # ... existing configuration ... + volumes: + - ./volumes/api/kong.yml:/home/kong/temp.yml: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 in `.env` + +Edit your `.env` file to use HTTPS with the Kong HTTPS port: + +``` +SUPABASE_PUBLIC_URL=https://:8443 +API_EXTERNAL_URL=https://:8443 +SITE_URL=https://:8443 +``` + +### Step 4: Restart and verify + +```sh +docker compose down && docker compose up -d +``` + +```sh +curl -I -k https://: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](https://letsencrypt.org/docs/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 Kong 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 +- Verify the callback URL registered with your OAuth provider matches `API_EXTERNAL_URL` followed by `/auth/v1/callback` +- After changing `API_EXTERNAL_URL`, restart all services with `docker compose down && docker compose up -d` + +### 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 + +- [Caddy documentation](https://caddyserver.com/docs/) +- [Nginx documentation](https://nginx.org/en/docs/) (on nginx.org) +- [docker-nginx-certbot on GitHub](https://github.com/JonasAlfredsson/docker-nginx-certbot) diff --git a/docker/.env.example b/docker/.env.example index af4a4d34e9a..a67cc5ef88f 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -46,7 +46,7 @@ S3_PROTOCOL_ACCESS_KEY_SECRET=850181e4652dd023b7a98c58ae0d2d34bd487ee0cc3254aed6 # URLs - Configure hostnames below to reflect your actual domain name ############ -# Access Dashboard and REST API +# Access to Dashboard and REST API SUPABASE_PUBLIC_URL=http://localhost:8000 # Full external URL of the Auth service, used to construct OAuth callbacks, diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 6dc1dd0ab55..21b66c6f802 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -75,6 +75,8 @@ services: volumes: # https://github.com/supabase/supabase/issues/12661 - ./volumes/api/kong.yml:/home/kong/temp.yml:ro,z + #- ./volumes/api/server.crt:/home/kong/server.crt:ro + #- ./volumes/api/server.key:/home/kong/server.key:ro depends_on: analytics: condition: service_healthy @@ -86,6 +88,8 @@ services: KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth,request-termination,ip-restriction KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k + #KONG_SSL_CERT: /home/kong/server.crt + #KONG_SSL_CERT_KEY: /home/kong/server.key SUPABASE_ANON_KEY: ${ANON_KEY} SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY} DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}