Problem: Keycloak "ERR_TOO_MANY_REDIRECTS" Behind HAProxy

Keycloak “ERR_TOO_MANY_REDIRECTS” Behind HAProxy

Problem

The Keycloak administration console is not accessible from enxternal network when accessed through HAProxy, the browser returns ERR_TOO_MANY_REDIRECTS, preventing access to the application.

The issue can be reproduced using curl, which reveals an endless sequence of HTTP 301 redirects. Instead, the behavior indicates a reverse proxy configuration issue, most likely related to the communication between HAProxy and the backend service.

When accessed externally, the browser displayed:

ERR_TOO_MANY_REDIRECTS


Root Cause

HAProxy backend configuration:

server <redacted-server-name> <redacted-private-ip>:80 id 108

If the intended configuration is to forward requests with:

X-Forwarded-Proto: https
X-Forwarded-Port: 443

the question arises:

If HAProxy explicitly forwards the request as HTTPS on port 443, why is the backend server configured to use HTTP (port 80)?


Investigation

The browser reported “ERR_TOO_MANY_REDIRECTS”, which could also be reproduced using curl.

curl -I https://<redacted-hostname>/admin/master/console/ -L

Output (excerpt):

HTTP/2 301
Server: Apache
Location: https://<redacted-hostname>/admin/master/console/

HTTP/2 301
Server: Apache
Location: https://<redacted-hostname>/admin/master/console/

HTTP/2 301
Server: Apache
Location: https://<redacted-hostname>/admin/master/console/

...

curl: (47) Maximum (50) redirects followed

Since direct access and authentication were working internally, as described, the issue did not initially appear to be caused by a UCS or Keycloak misconfiguration.

Is it possible to configure the HAProxy backend to send the X-Forwarded-Proto header?

When performing the following test:

curl -I \
  -H "X-Forwarded-Proto: https" \
  https://<redacted-hostname>/admin/master/console/

the redirect loop no longer occurred.

Please verify the HAProxy backend configuration for the Keycloak service and ensure the forwarding headers are configured as follows:

backend keycloak_backend
    http-request set-header X-Forwarded-Proto https
    http-request set-header X-Forwarded-Port 443

    # Optional but recommended
    http-request set-header X-Forwarded-For %[src]

    server keycloak-backend <backend-ip>:443 ssl verify none

    # Alternatively, if the backend intentionally uses HTTP:
    # server keycloak-backend <backend-ip>:80

Please also verify the following UCR variable and provide its output:

ucr get apache2/force_https

HAProxy Configuration

The frontend already contained the following configuration:

http-request set-header X-Forwarded-Proto http if !https
http-request set-header X-Forwarded-Proto https if https

The backend was updated with the following directives:

http-request set-header X-Forwarded-Proto https
http-request set-header X-Forwarded-Port 443
http-request set-header X-Forwarded-For %[src]

The backend server configuration remained:

server keycloak-backend <backend-ip>:80 id 108

Solution

Modify the backend server configuration to use HTTPS instead of HTTP:

server keycloak-backend <backend-ip>:443 ssl verify none id 108

Reload HAProxy to apply the updated configuration.

Finally, verify the behavior by executing:

curl -I https://<redacted-hostname>/admin/master/console/ -L

Additional

Using ssl verify none means that HAProxy establishes a TLS connection to the backend but does not verify whether the certificate is signed by a trusted Certificate Authority or whether the certificate matches the backend hostname.

The backend system (<backend-ip>:443) presents a certificate signed by the internal UCS Root CA (/etc/univention/ssl/ucsCA/CAcert.pem). Since HAProxy does not trust this CA by default, removing verify none without importing the CA would cause TLS handshake failures, resulting in a 502 Bad Gateway instead of a redirect loop.

Instead of using verify none, the recommended approach is to import the UCS Root CA into HAProxy’s trust store:

scp root@<backend-ip>:/etc/univention/ssl/ucsCA/CAcert.pem \
    /var/etc/haproxy/ucs-ca.pem

After importing the CA certificate, the backend configuration can be changed to perform proper certificate validation instead of relying on:

ssl verify none