fix(self-hosted): change default api external url to contain /auth/v1 (#47640)

This commit is contained in:
Andrey A. authored and GitHub committed 2026-07-07 12:28:47 +02:00
1 parent 689b6991f0
commit e7abda8dce
11 files changed
+64 -45

No files matched your search

@@ -229,7 +229,7 @@ For a description of all secrets refer to the [related section](#configuring-sec
Review and change URL configuration variables:
- `SUPABASE_PUBLIC_URL`: base URL for accessing Supabase from the Internet (Dashboard, API, Storage, etc.), e.g., `http://example.com:8000`
- `API_EXTERNAL_URL`: used by the Auth service to configure callback URLs, e.g., `http://example.com:8000`
- `API_EXTERNAL_URL`: used by the Auth service to configure callback URLs, e.g., `http://example.com:8000/auth/v1`
- `SITE_URL`: default [redirect URL](/docs/guides/auth/redirect-urls) for Auth, e.g., `http://example.com:3000`
<Admonition type="note" title="What your-domain means in the docs">
@@ -134,8 +134,8 @@ Routes are matched in the order declared. The first matching prefix wins. Protec
| `/auth/v1/authorize` | auth | `/authorize` | Open | OAuth authorize |
| `/auth/v1/.well-known/jwks.json` | auth | `/.well-known/jwks.json` | Open | JWKS for third-party verification |
| `/.well-known/oauth-authorization-server` | auth | - | Open | OAuth 2.0 Authorization Server Metadata (RFC 8414) |
| `/sso/saml/acs` | auth | - | Open | SAML assertion consumer |
| `/sso/saml/metadata` | auth | - | Open | SAML metadata |
| `/auth/v1/sso/saml/acs` | auth | `/sso/saml/acs` | Open | SAML assertion consumer |
| `/auth/v1/sso/saml/metadata` | auth | `/sso/saml/metadata` | Open | SAML metadata |
| `/functions/v1/` | functions | `/` | Bypass | Edge Functions runtime performs its own JWT verification; 150s timeout |
| `/storage/v1/` | storage | `/` | Bypass | Storage performs its own authorization |
| `/auth/v1/` | auth | `/` | API key | Protected Auth endpoints |
@@ -11,7 +11,7 @@ This guide covers the **server-side configuration** required to enable social lo
You need:
- A working self-hosted Supabase installation. See [Self-Hosting with Docker](/docs/guides/self-hosting/docker).
- `API_EXTERNAL_URL` set to the publicly reachable URL of your Supabase instance (e.g., `https://<your-domain>`).
- `API_EXTERNAL_URL` set to the publicly reachable URL of your Supabase instance (e.g., `https://<your-domain>/auth/v1`).
<Admonition type="danger">
@@ -79,7 +79,7 @@ auth:
GOTRUE_EXTERNAL_GOOGLE_ENABLED: ${GOOGLE_ENABLED}
GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
GOTRUE_EXTERNAL_GOOGLE_SECRET: ${GOOGLE_SECRET}
GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
```
<Admonition type="note">
@@ -149,7 +149,7 @@ auth:
GOTRUE_EXTERNAL_GOOGLE_ENABLED: ${GOOGLE_ENABLED}
GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
GOTRUE_EXTERNAL_GOOGLE_SECRET: ${GOOGLE_SECRET}
GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
```
</TabPanel>
@@ -178,7 +178,7 @@ auth:
GOTRUE_EXTERNAL_GITHUB_ENABLED: ${GITHUB_ENABLED}
GOTRUE_EXTERNAL_GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID}
GOTRUE_EXTERNAL_GITHUB_SECRET: ${GITHUB_SECRET}
GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
```
</TabPanel>
@@ -213,7 +213,7 @@ auth:
GOTRUE_EXTERNAL_AZURE_ENABLED: ${AZURE_ENABLED}
GOTRUE_EXTERNAL_AZURE_CLIENT_ID: ${AZURE_CLIENT_ID}
GOTRUE_EXTERNAL_AZURE_SECRET: ${AZURE_SECRET}
GOTRUE_EXTERNAL_AZURE_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
GOTRUE_EXTERNAL_AZURE_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
## Optional: uncomment for tenant-specific Azure login
# GOTRUE_EXTERNAL_AZURE_URL: ${AZURE_URL}
```
@@ -241,7 +241,7 @@ auth:
GOTRUE_EXTERNAL_APPLE_ENABLED: ${APPLE_ENABLED}
GOTRUE_EXTERNAL_APPLE_CLIENT_ID: ${APPLE_CLIENT_ID}
GOTRUE_EXTERNAL_APPLE_SECRET: ${APPLE_SECRET}
GOTRUE_EXTERNAL_APPLE_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
GOTRUE_EXTERNAL_APPLE_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
```
<Admonition type="note">
@@ -280,7 +280,7 @@ auth:
GOTRUE_EXTERNAL_KEYCLOAK_ENABLED: ${KEYCLOAK_ENABLED}
GOTRUE_EXTERNAL_KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID}
GOTRUE_EXTERNAL_KEYCLOAK_SECRET: ${KEYCLOAK_SECRET}
GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
GOTRUE_EXTERNAL_KEYCLOAK_URL: ${KEYCLOAK_URL}
```
@@ -404,7 +404,7 @@ Run `docker compose exec auth env | grep GOTRUE_EXTERNAL` to verify the variable
After a successful OAuth login, the Auth service redirects to `SITE_URL` or a URL from `ADDITIONAL_REDIRECT_URLS`. Ensure:
- `SITE_URL` in `.env` is set to your application's URL
- `SITE_URL` in `.env` is set to your **application's URL**
- If your app uses a different redirect URL, add it to `ADDITIONAL_REDIRECT_URLS` (comma-separated)
{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */}
@@ -443,7 +443,7 @@ docker compose logs auth
Common causes:
- Missing required environment variable (e.g., `CLIENT_ID` or `SECRET` is empty)
- Invalid `API_EXTERNAL_URL` (must be a valid URL with protocol)
- Invalid `API_EXTERNAL_URL` (must be a valid URL - including protocol and ending with `/auth/v1`)
## Environment variable reference
@@ -454,7 +454,7 @@ All OAuth-related environment variables for the `auth` service in `docker-compos
| `GOTRUE_EXTERNAL_*_ENABLED` | Enable the provider (`true`/`false`) | Yes |
| `GOTRUE_EXTERNAL_*_CLIENT_ID` | OAuth client ID from the provider | Yes |
| `GOTRUE_EXTERNAL_*_SECRET` | OAuth client secret from the provider | Yes |
| `GOTRUE_EXTERNAL_*_REDIRECT_URI` | Callback URL: `${API_EXTERNAL_URL}/auth/v1/callback` | Yes |
| `GOTRUE_EXTERNAL_*_REDIRECT_URI` | Callback URL: `${API_EXTERNAL_URL}/callback` | Yes |
| `GOTRUE_SITE_URL` | Default redirect URL after authentication (set via `SITE_URL` in `.env`) | Yes |
## Additional resources
@@ -45,10 +45,16 @@ Update the URL configuration in your `.env` file to use your HTTPS domain:
```sh name=.env
SUPABASE_PUBLIC_URL=https://<your-domain>
API_EXTERNAL_URL=https://<your-domain>
SITE_URL=https://<your-domain>
API_EXTERNAL_URL=https://<your-domain>/auth/v1
SITE_URL=https://<your-app-domain>
```
<Admonition type="note" title="What your-app-domain means">
`<your-app-domain>` is the URL of your own frontend application (where users land after signing in) - not your Supabase instance. It's often a different domain, and possibly a different service entirely, from `<your-domain>` used above.
</Admonition>
For Nginx, change the following to your domain name and a **valid** email address:
```sh name=.env
@@ -167,8 +173,8 @@ Edit your `.env` file to use HTTPS with the Kong HTTPS port:
```sh name=.env
SUPABASE_PUBLIC_URL=https://<your-domain>:8443
API_EXTERNAL_URL=https://<your-domain>:8443
SITE_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
@@ -205,8 +211,8 @@ If Realtime subscriptions fail to connect:
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`
- 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 `docker compose down && docker compose up -d`
### Mixed content warnings
@@ -211,7 +211,7 @@ Setting a bucket to "Public" only allows unauthenticated **downloads**. Uploads
### Upload URLs point to localhost
If uploads from a browser fail (CORS or mixed content errors), check that `API_EXTERNAL_URL` and `SUPABASE_PUBLIC_URL` in your `.env` file match your actual domain and protocol - not `http://localhost:8000`.
If uploads from a browser fail (CORS or mixed content errors), check that `SUPABASE_PUBLIC_URL` in your `.env` file matches your actual domain and protocol - not `http://localhost:8000`.
### Additional resources
@@ -23,7 +23,7 @@ You need:
- Open SSL installed (for key generation)
- Your IdP's SAML metadata URL or metadata XML
- The `SERVICE_ROLE_KEY` from your `.env` file (needed for admin API calls)
- `API_EXTERNAL_URL` set to the publicly-accessible URL of your Supabase Auth service (e.g., `https://<your-domain>`). This URL is used as the SAML Service Provider entity ID and for constructing the ACS endpoint URL
- `API_EXTERNAL_URL` set to the publicly-accessible URL of your Supabase Auth service (e.g., `https://<your-domain>/auth/v1`). Used as the base for constructing the SAML Service Provider entity ID and ACS endpoint URL
## How SAML SSO works in Supabase
@@ -37,7 +37,7 @@ The login flow works as follows:
1. Your app calls `POST /auth/v1/sso` with a domain or provider_id
2. Auth generates a SAML `AuthnRequest` and returns a redirect URL to the IdP
3. The user authenticates at the IdP
4. The IdP POSTs a SAML Response to `POST /sso/saml/acs`
4. The IdP POSTs a SAML Response to `POST /auth/v1/sso/saml/acs`
5. Auth validates the assertion, creates or links the user, and issues a session
6. The user is redirected back to your app with session tokens
@@ -89,10 +89,6 @@ SAML_PRIVATE_KEY=<your-base64-encoded-private-key>
# Optional: how long relay state tokens remain valid (default: 2m0s)
# SAML_RELAY_STATE_VALIDITY_PERIOD=2m0s
# Optional: override the SAML entity ID / ACS base URL
# Defaults to API_EXTERNAL_URL if not set
# SAML_EXTERNAL_URL=https://supabase.example.com:8000
# Optional: rate limit on the ACS endpoint (requests per second, default: 15)
# SAML_RATE_LIMIT_ASSERTION=15
```
@@ -111,7 +107,6 @@ auth:
GOTRUE_SAML_PRIVATE_KEY: ${SAML_PRIVATE_KEY}
# GOTRUE_SAML_ALLOW_ENCRYPTED_ASSERTIONS: ${SAML_ALLOW_ENCRYPTED_ASSERTIONS}
# GOTRUE_SAML_RELAY_STATE_VALIDITY_PERIOD: ${SAML_RELAY_STATE_VALIDITY_PERIOD}
# GOTRUE_SAML_EXTERNAL_URL: ${SAML_EXTERNAL_URL}
# GOTRUE_SAML_RATE_LIMIT_ASSERTION: ${SAML_RATE_LIMIT_ASSERTION}
```
@@ -137,7 +132,7 @@ Once SAML is enabled, your Supabase instance exposes service provider (SP) metad
Verify it using `curl`:
```sh
curl http://<your-domain>/sso/saml/metadata
curl http://<your-domain>/auth/v1/sso/saml/metadata
```
This returns an XML document containing your SP entity ID, ACS endpoint URL, and signing certificate. You will need to provide this to your IdP.
@@ -304,6 +299,21 @@ On the IdP side, create a new SAML application and configure it with your SP det
</TabPanel>
<TabPanel id="auth0" label="Auth0">
**Auth0:**
- Dashboard → Applications → Applications → Create Application → Regular Web Application
- Settings tab → Advanced Settings → Endpoints tab
- Copy the **SAML Metadata URL** (this is the IdP metadata URL for Supabase)
- Settings tab → Advanced Settings → Add-ons → SAML2 Web App
- Enable SAML2 Web App add-on
- Settings tab in the add-on:
- Application Callback URL (ACS): `{API_EXTERNAL_URL}/sso/saml/acs`
- Enable the add-on
</TabPanel>
</Tabs>
## Attribute mapping
@@ -528,7 +538,6 @@ The response should include `app_metadata.provider: "sso:saml"` and any mapped a
| `SAML_PRIVATE_KEY` | - | Base64-encoded PKCS#1 RSA private key (min 2048-bit). Used to sign SAML requests and optionally decrypt assertions. |
| `SAML_ALLOW_ENCRYPTED_ASSERTIONS` | `false` | Accept encrypted SAML assertions from IdPs |
| `SAML_RELAY_STATE_VALIDITY_PERIOD` | `2m0s` | How long relay state tokens remain valid. Increase if users on slow networks time out during the IdP redirect. |
| `SAML_EXTERNAL_URL` | `API_EXTERNAL_URL` | Override the base URL used for the SAML entity ID and ACS endpoint. Only needed if the SAML endpoints are served on a different URL than the rest of the Auth API. |
| `SAML_RATE_LIMIT_ASSERTION` | `15` | Maximum ACS requests per second. Protects against assertion replay floods. |
{/* supa-mdx-lint-enable Rule003Spelling */}
@@ -560,8 +569,8 @@ base64 -w 0 -i pk_rsa1.der
### IdP cannot reach the ACS endpoint
- Verify `API_EXTERNAL_URL` is set to a URL the IdP can reach (not `localhost` unless testing locally)
- Check that the API gateway routes for `/sso/saml/acs` and `/sso/saml/metadata` are configured as open (no `key-auth` plugin).
- Verify `API_EXTERNAL_URL` is set to a URL containing `/auth/v1` the IdP can reach (not set to `localhost` unless testing locally)
- Check that the API gateway routes for `/auth/v1/sso/saml/acs` and `/auth/v1/sso/saml/metadata` are configured as open (no `key-auth` plugin).
- Check the Auth container logs: `docker compose logs auth`
### "No SSO provider found for this domain"
+2 -2
View File
@@ -98,7 +98,7 @@ SUPABASE_PUBLIC_URL=http://localhost:8000
# Full external URL of the Auth service, used to construct OAuth callbacks,
# SAML endpoints, and email links
API_EXTERNAL_URL=http://localhost:8000
API_EXTERNAL_URL=http://localhost:8000/auth/v1
# See also the Auth section below for Site URL and Redirect URLs configuration
@@ -256,7 +256,7 @@ ENABLE_PHONE_AUTOCONFIRM=true
# Optional: override the SAML entity ID / ACS base URL
# Defaults to API_EXTERNAL_URL if not set
# SAML_EXTERNAL_URL=https://supabase.example.com:8000
# SAML_EXTERNAL_URL=https://supabase.example.com:8000/auth/v1
# Optional: rate limit on the ACS endpoint (requests per second, default: 15)
# SAML_RATE_LIMIT_ASSERTION=15
+7 -5
View File
@@ -156,7 +156,7 @@ services:
# For Podman, use: GOTRUE_JWT_KEYS: ${JWT_KEYS}
#GOTRUE_JWT_KEYS: ${JWT_KEYS:-[]}
GOTRUE_JWT_ISSUER: ${API_EXTERNAL_URL}/auth/v1
GOTRUE_JWT_ISSUER: ${API_EXTERNAL_URL}
GOTRUE_EXTERNAL_EMAIL_ENABLED: ${ENABLE_EMAIL_SIGNUP}
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: ${ENABLE_ANONYMOUS_USERS}
@@ -185,17 +185,17 @@ services:
# GOTRUE_EXTERNAL_GOOGLE_ENABLED: ${GOOGLE_ENABLED}
# GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
# GOTRUE_EXTERNAL_GOOGLE_SECRET: ${GOOGLE_SECRET}
# GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
# GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
# GOTRUE_EXTERNAL_GITHUB_ENABLED: ${GITHUB_ENABLED}
# GOTRUE_EXTERNAL_GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID}
# GOTRUE_EXTERNAL_GITHUB_SECRET: ${GITHUB_SECRET}
# GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
# GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
# GOTRUE_EXTERNAL_AZURE_ENABLED: ${AZURE_ENABLED}
# GOTRUE_EXTERNAL_AZURE_CLIENT_ID: ${AZURE_CLIENT_ID}
# GOTRUE_EXTERNAL_AZURE_SECRET: ${AZURE_SECRET}
# GOTRUE_EXTERNAL_AZURE_REDIRECT_URI: ${API_EXTERNAL_URL}/auth/v1/callback
# GOTRUE_EXTERNAL_AZURE_REDIRECT_URI: ${API_EXTERNAL_URL}/callback
# Uncomment to configure SMS delivery (phone auth and phone MFA).
# GOTRUE_SMS_PROVIDER: ${SMS_PROVIDER}
@@ -220,12 +220,14 @@ services:
# GOTRUE_MFA_MAX_ENROLLED_FACTORS: ${MFA_MAX_ENROLLED_FACTORS}
# SAML SSO
# See: https://supabase.com/docs/guides/self-hosting/self-hosted-saml-sso
# GOTRUE_SAML_ENABLED: ${SAML_ENABLED}
# GOTRUE_SAML_PRIVATE_KEY: ${SAML_PRIVATE_KEY}
# GOTRUE_SAML_ALLOW_ENCRYPTED_ASSERTIONS: ${SAML_ALLOW_ENCRYPTED_ASSERTIONS}
# GOTRUE_SAML_RELAY_STATE_VALIDITY_PERIOD: ${SAML_RELAY_STATE_VALIDITY_PERIOD}
# GOTRUE_SAML_EXTERNAL_URL: ${SAML_EXTERNAL_URL}
# GOTRUE_SAML_RATE_LIMIT_ASSERTION: ${SAML_RATE_LIMIT_ASSERTION}
# Optional, defaults to API_EXTERNAL_URL if not set
# GOTRUE_SAML_EXTERNAL_URL: ${SAML_EXTERNAL_URL}
# Uncomment to enable custom access token hook.
# See: https://supabase.com/docs/guides/auth/auth-hooks for
+1 -1
View File
@@ -290,7 +290,7 @@ else
fi
public_url=$(ask "SUPABASE_PUBLIC_URL (Studio + APIs)" "$current_public_url")
api_url=$(ask "API_EXTERNAL_URL (Auth callbacks)" "$public_url")
api_url=$(ask "API_EXTERNAL_URL (Auth callbacks)" "$public_url/auth/v1")
site_url=$(ask "SITE_URL (default Auth redirect)" "$current_site_url")
# Suggest PROXY_DOMAIN from the public_url host (unless it's localhost-ish)
+6 -4
View File
@@ -201,14 +201,15 @@ resources:
- any: true
- match:
prefix: /sso/saml/acs
prefix: /auth/v1/sso/saml/acs
route:
cluster: auth
prefix_rewrite: /sso/saml/acs
timeout: 30s
request_headers_to_add:
- header:
key: X-Forwarded-Prefix
value: /sso/saml/acs
value: /auth/v1/sso/saml/acs
append_action: ADD_IF_ABSENT
typed_per_filter_config:
envoy.filters.http.basic_auth:
@@ -229,14 +230,15 @@ resources:
- any: true
- match:
prefix: /sso/saml/metadata
prefix: /auth/v1/sso/saml/metadata
route:
cluster: auth
prefix_rewrite: /sso/saml/metadata
timeout: 30s
request_headers_to_add:
- header:
key: X-Forwarded-Prefix
value: /sso/saml/metadata
value: /auth/v1/sso/saml/metadata
append_action: ADD_IF_ABSENT
typed_per_filter_config:
envoy.filters.http.basic_auth:
+2 -2
View File
@@ -84,7 +84,7 @@ services:
- name: auth-v1-open-sso-acs
strip_path: true
paths:
- /sso/saml/acs
- /auth/v1/sso/saml/acs
plugins:
- name: cors
@@ -94,7 +94,7 @@ services:
- name: auth-v1-open-sso-metadata
strip_path: true
paths:
- /sso/saml/metadata
- /auth/v1/sso/saml/metadata
plugins:
- name: cors