Keycloak Account Console Redirect Loop Behind Nginx

Or: Why Your Cookies Are Having an Identity Crisis

So you’ve set up Keycloak behind Nginx, everything works beautifully – login, logout, token refresh – and then someone clicks “Manage Account” and suddenly your browser is doing its best impression of a hamster wheel. Welcome to the wonderful world of Keycloak Account Console redirect loops.

Fear not, fellow DevOps warrior. After hours of staring at logs that just kept screaming REFRESH_TOKEN_ERROR, we finally cracked this case. Grab your favorite caffeinated beverage, and let’s dive in.


The Symptoms

“It works on my machine” – said no reverse proxy ever

You’ll recognize this problem when:

  • The Keycloak Account Console (/realms/{realm}/account) loads… and loads… and loads
  • Your browser’s Network tab looks like a rave with endless redirects
  • Keycloak logs are filled with this beauty:
WARN type="REFRESH_TOKEN_ERROR", realmId="...", clientId="account-console", 
     error="invalid_token", reason="Invalid refresh token"
  • Eventually, Nginx throws a 429 Too Many Requests because even it’s tired of your shenanigans
  • Your browser’s localStorage is polluted with hundreds of kc-callback-* entries

Fun fact: The Account Console is a React SPA that uses PKCE flow. It stores tokens in localStorage and relies heavily on cookies being passed correctly. When cookies get mangled by your reverse proxy, the refresh token appears “invalid” because it was never properly stored in the first place.


The Root Cause

When your proxy loves cookies a little too much

The culprit? Nginx cookie manipulation directives that override Keycloak’s carefully crafted cookie attributes.

The most common offenders:

Directive What it does Why it breaks Keycloak
proxy_cookie_path ~(.*) "$1; SameSite=strict" Rewrites ALL cookie paths with SameSite=strict OAuth redirects need SameSite=Lax or None
proxy_cookie_flags ~ secure httponly samesite=none Overrides cookie flags globally Conflicts with Keycloak’s own cookie settings
Missing proxy_pass_header Set-Cookie Cookies might not be forwarded properly Refresh token never reaches the browser

Pro tip: SameSite=strict is the enemy of OAuth flows. When the browser redirects from Keycloak back to your app with an auth code, SameSite=strict cookies won’t be sent. No cookies = no session = infinite loop of sadness.


The Fix

Let Keycloak be Keycloak

Here’s a working Nginx configuration for Keycloak that respects cookie boundaries:

upstream keycloak_backend {
    server your-keycloak:8443;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name auth.yourdomain.com;

    1. SSL config...

    location / {
        proxy_pass https://keycloak_backend;
        proxy_http_version 1.1;

        1. Essential headers for Keycloak
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Port 443;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-SSL on;
        proxy_set_header Connection "";

        1. THE MAGIC TRIO - Don't touch the cookies!
        proxy_pass_header Set-Cookie;
        proxy_set_header Cookie $http_cookie;
        proxy_buffering off;

        1. DO NOT add these for Keycloak:
        1. proxy_cookie_path ...
        1. proxy_cookie_flags ...

        proxy_ssl_verify off;
        proxy_ssl_server_name on;
        proxy_ssl_name $host;
    }
}

The Magic Trio Explained

Directive Purpose
proxy_pass_header Set-Cookie Ensures Set-Cookie headers from Keycloak reach the browser
proxy_set_header Cookie $http_cookie Forwards all cookies from browser to Keycloak
proxy_buffering off Prevents response buffering that might interfere with auth flows

Debugging Checklist

For when things still don’t work

1. Check Keycloak Logs

docker logs keycloak 2>&1 | grep -i "error\|warn" | tail -50

Look for:

  • REFRESH_TOKEN_ERROR – Cookie/token issue
  • cookie_not_found – Cookies not being forwarded
  • invalid_redirect_uri – Client misconfiguration

2. Verify Cookie Headers

curl -v -k https://auth.yourdomain.com/realms/your-realm/account/ 2>&1 | grep -i "set-cookie\|cookie"

You should see multiple Set-Cookie headers from Keycloak.

3. Check for Global Nginx Directives

grep -r "proxy_cookie" /etc/nginx/

If you find global cookie manipulation rules, either remove them or exclude your Keycloak server block.

4. Clear Browser State

In browser DevTools console:

// Nuclear option - clear all Keycloak localStorage entries
Object.keys(localStorage)
  .filter(k => k.startsWith('kc-'))
  .forEach(k => localStorage.removeItem(k));
location.reload();

5. Test in Incognito

Always test in a fresh incognito window to eliminate cached cookies/tokens.


Quick Reference Card

┌─────────────────────────────────────────────────────────────────┐
│           KEYCLOAK + NGINX COOKIE SURVIVAL GUIDE                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ✅ DO:                                                         │
│     • proxy_pass_header Set-Cookie                              │
│     • proxy_set_header Cookie $http_cookie                      │
│     • proxy_buffering off                                       │
│     • Let Keycloak manage its own cookie attributes             │
│                                                                 │
│  ❌ DON'T:                                                      │
│     • proxy_cookie_path with SameSite=strict                    │
│     • proxy_cookie_flags that override Keycloak                 │
│     • Global cookie manipulation rules                          │
│                                                                 │
│  🔍 DEBUG:                                                      │
│     • Check logs: docker logs keycloak | grep ERROR             │
│     • Verify cookies: curl -v | grep -i cookie                  │
│     • Test in incognito mode                                    │
│     • Clear localStorage: kc-* entries                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Appendix: Common Errors & Solutions

Error Likely Cause Fix Severity
REFRESH_TOKEN_ERROR: Invalid refresh token Cookie manipulation by proxy Remove proxy_cookie_* directives 🔴
cookie_not_found Cookies not forwarded Add proxy_pass_header Set-Cookie 🔴
invalid_redirect_uri Client misconfiguration Check Valid Redirect URIs in Keycloak 🟠
already_logged_in redirect loop SameSite=strict blocking cookies Remove SameSite override 🔴
Account Console shows spinner forever localStorage pollution Clear kc-* entries 🟡
429 Too Many Requests Redirect loop hitting rate limit Fix the underlying cookie issue 🟠

Conclusion

The Keycloak Account Console redirect loop is almost always a cookie handling issue at the reverse proxy level. The Account Console’s React SPA is particularly sensitive because it:

  1. Uses PKCE flow (requires multiple redirects)
  2. Stores tokens in localStorage
  3. Relies on session cookies for token refresh
  4. Has strict expectations about cookie attributes

Key takeaways:

  • 🍪 Never override Keycloak’s cookie attributes with global proxy rules
  • 🔀 SameSite=strict kills OAuth flows – use Lax or None
  • 📡 Always forward Set-Cookie headers from Keycloak to browser
  • 🧹 Clear localStorage when debugging – those kc-callback-* entries are cursed

The next time your Keycloak Account Console decides to become a redirect tornado, remember: it’s probably your cookies. It’s always the cookies.


Stay authenticated, stay curious. 🔐


Related Resources:

Leave a Reply

Your email address will not be published. Required fields are marked *