Skip to main content

HA Reverse Proxy and FQDN

Applies to installs that are accessed through an external URL (for example https://proxcenter.example.com behind nginx/Traefik/Caddy with TLS, with or without OIDC/SSO) rather than a bare IP.

What the conversion preserves​

  • NEXTAUTH_URL (your external URL) survives the conversion: the wizard merges the HA settings into the existing .env instead of replacing it, and the HA compose only falls back to http://<VIP>:3000 when no NEXTAUTH_URL exists (fresh IP-only installs).
  • The frontend exempts the external URL's host from the VIP redirect, so requests arriving through your proxy are served, not bounced.
  • Keeping the same external NEXTAUTH_URL keeps the registered OIDC callback (<NEXTAUTH_URL>/api/auth/callback/oidc) and post-logout URL valid. Verify sign-in and sign-out through that public URL after the conversion.

The one required admin action​

Repoint the reverse proxy's upstream at the VIP:

# before (single node) # after (HA)
upstream -> http://<NODE1_IP>:3000 upstream -> http://<VIP>:3000
Before and after the high-availability conversion, with a list of changes between them. In both, Users reach the Reverse proxy at https://proxcenter.example.com, which terminates TLS, and the identity provider's OIDC callback goes to the same proxy. Before, the proxy upstream is Node 1 at NODE1_IP:3000. After, it is the VIP on port 3000, which keepalived moves to a healthy node and which is held by one of Node 1, Node 2 and Node 3. The changes listed are: upstream rewired from proxy to Node 1 to proxy to VIP, upstream changed from http://<NODE1_IP>:3000 to http://<VIP>:3000, and the VIP, its holder link, Node 2 and Node 3 added. A note says NEXTAUTH_URL stays https://proxcenter.example.com on the three nodes, so the OIDC callback <NEXTAUTH_URL>/api/auth/callback/oidc registered at the identity provider stays valid.Before and after the high-availability conversion, with a list of changes between them. In both, Users reach the Reverse proxy at https://proxcenter.example.com, which terminates TLS, and the identity provider's OIDC callback goes to the same proxy. Before, the proxy upstream is Node 1 at NODE1_IP:3000. After, it is the VIP on port 3000, which keepalived moves to a healthy node and which is held by one of Node 1, Node 2 and Node 3. The changes listed are: upstream rewired from proxy to Node 1 to proxy to VIP, upstream changed from http://<NODE1_IP>:3000 to http://<VIP>:3000, and the VIP, its holder link, Node 2 and Node 3 added. A note says NEXTAUTH_URL stays https://proxcenter.example.com on the three nodes, so the OIDC callback <NEXTAUTH_URL>/api/auth/callback/oidc registered at the identity provider stays valid.
The only change after the conversion is the proxy upstream, from node 1 to the VIP. The public URL, TLS and the OIDC callback stay as they are.

The VIP follows the healthy nodes automatically; the proxy needs no health logic of its own. Keep TLS termination at the proxy exactly as before.

Environment variables​

Set in .env on all 3 nodes (the wizard distributes node 1's values):

VariablePurpose
NEXTAUTH_URLThe preserved external URL. Its host is ALWAYS exempt from the VIP redirect.
HA_REDIRECT_EXEMPT_HOSTSOptional, comma-separated extra hostnames that must never be VIP-redirected (e.g. a second proxy name, an internal alias). Hostnames only, no scheme or port.
HA_REDIRECT_DISABLEDBreak-glass: true disables the VIP redirect entirely. Use only for debugging; nodes then serve any Host header directly.

Example:

NEXTAUTH_URL=https://proxcenter.example.com
# HA_REDIRECT_EXEMPT_HOSTS=proxcenter-alias.internal,proxcenter.dmz.example.com
# HA_REDIRECT_DISABLED=true

How the redirect decides​

Flowchart of the VIP redirect on a high-availability node. A request to a node is served by the node that received it if Redirect off? is yes (HA_REDIRECT_DISABLED set to true on the node); otherwise if Exempt host? is yes, the Host header, port stripped and lowercased, being the VIP, localhost, 127.0.0.1, the NEXTAUTH_URL host or a host listed in HA_REDIRECT_EXEMPT_HOSTS; otherwise if Exempt path? is yes: /api/health and below, /api/v1/ha/, and the ACME challenges under /.well-known/acme-challenge/ and /api/acme/challenge/. Any other request gets a 302 to the VIP with the same path and query: https://<VIP> with the APP_URL port if APP_URL is https, http://<VIP>:3000 otherwise. A note says that through your reverse proxy the FQDN is the NEXTAUTH_URL host, so there is no redirect and the address bar keeps it.Flowchart of the VIP redirect on a high-availability node. A request to a node is served by the node that received it if Redirect off? is yes (HA_REDIRECT_DISABLED set to true on the node); otherwise if Exempt host? is yes, the Host header, port stripped and lowercased, being the VIP, localhost, 127.0.0.1, the NEXTAUTH_URL host or a host listed in HA_REDIRECT_EXEMPT_HOSTS; otherwise if Exempt path? is yes: /api/health and below, /api/v1/ha/, and the ACME challenges under /.well-known/acme-challenge/ and /api/acme/challenge/. Any other request gets a 302 to the VIP with the same path and query: https://<VIP> with the APP_URL port if APP_URL is https, http://<VIP>:3000 otherwise. A note says that through your reverse proxy the FQDN is the NEXTAUTH_URL host, so there is no redirect and the address bar keeps it.
Only a request that passes none of the three checks is redirected to the VIP. Your proxy's host is exempt through NEXTAUTH_URL, so its users stay on the public URL.

For any request, the frontend compares the port-stripped, lowercased Host header against: the VIP, localhost/127.0.0.1, the NEXTAUTH_URL host and the HA_REDIRECT_EXEMPT_HOSTS list. Anything else is redirected (302) to http://<VIP>:3000 with the path and query preserved. Health probes (/api/health, /api/health/live) and the HA API are never redirected.

Fresh IP-only installs​

Without an external URL, the application is served at http://<VIP>:3000 and the wizard's completion screen reminds you to update OIDC callback URLs if you add SSO later. Adding a proxy afterwards: set NEXTAUTH_URL in the .env of all 3 nodes, run docker compose -f docker-compose.ha.yml up -d frontend on each, then point the proxy at the VIP.

Verify the external access path​

After repointing the upstream:

  1. Open the existing HTTPS FQDN and confirm that its certificate and login page are correct.
  2. Sign in and open an authenticated page. The address should remain on the external FQDN, rather than switching to an internal node or the HTTP VIP.
  3. If you use OIDC, complete both sign-in and sign-out through the identity provider. Keep its callback and post-logout registrations aligned with NEXTAUTH_URL.
  4. Confirm that the proxy can reach the VIP and that long-lived connections, such as consoles, follow the proxy configuration described in Installation.

Preserving the public URL does not change which upstream the proxy uses; update and verify that route separately.