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 Requestsbecause 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 issuecookie_not_found– Cookies not being forwardedinvalid_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:
- Uses PKCE flow (requires multiple redirects)
- Stores tokens in localStorage
- Relies on session cookies for token refresh
- 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