Keycloak Login Loop: How to Fix Infinite Redirect Issues
Last updated: March 2026
Few issues are more frustrating than an infinite login loop. You click “Login,” Keycloak authenticates you, redirects you back to your application, and then your application immediately redirects you back to Keycloak. This cycle repeats endlessly, sometimes so fast you only see the browser tab loading indicator flickering.
The root cause is always the same: your application cannot establish a session after Keycloak authenticates the user. But the specific reason why the session fails can come from multiple sources: cookie configuration, reverse proxy settings, hostname mismatches, SameSite policies, or even browser extensions.
This guide covers every known cause of Keycloak login loops, shows how to diagnose each one, and provides working fixes.
Understanding the Login Flow
Before debugging, understand what happens during a normal OIDC login:
- User visits a protected page in your application
- Application redirects to Keycloak’s authorization endpoint
- User authenticates in Keycloak (enters credentials)
- Keycloak redirects back to your application with an authorization code
- Application exchanges the code for tokens (via back-channel)
- Application creates a session (sets a cookie)
- User sees the protected page
A login loop happens when step 6 fails. The application either cannot set the cookie, or the browser does not send the cookie on subsequent requests. Without a session, the application thinks the user is unauthenticated and redirects back to step 2.
Cause 1: SameSite Cookie Issues
This is the most common cause of login loops in modern browsers.
The Problem
Modern browsers enforce SameSite cookie policies. If your application sets a session cookie with SameSite=Strict or SameSite=Lax, and the redirect from Keycloak is considered a cross-site request, the browser will not send the cookie.
This happens when:
- Keycloak and your application are on different domains
- The redirect from Keycloak to your app is treated as a cross-site navigation
- Your session cookie does not have
SameSite=None; Secureset
Diagnosis
Open your browser developer tools:
- Go to the Application tab (Chrome) or Storage tab (Firefox)
- Look at Cookies for your application domain
- Check the
SameSiteattribute of the session cookie - Go to the Network tab, find the redirect request from Keycloak to your app, and check if the cookie is being sent (look for
Cookieheader)
Fix
For Express.js (Node.js):
app.use(session({
secret: 'your-secret',
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // Required when SameSite=None
httpOnly: true,
sameSite: 'none', // Allow cross-site cookie delivery
maxAge: 3600000,
},
}));
For Flask (Python):
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
For Spring Boot (Java):
server.servlet.session.cookie.same-site=none
server.servlet.session.cookie.secure=true
server.servlet.session.cookie.http-only=true
For PHP:
session_set_cookie_params([
'samesite' => 'None',
'secure' => true,
'httponly' => true,
]);
SameSite=None requires Secure=true, which means HTTPS. If you are developing locally without HTTPS, use SameSite=Lax and make sure Keycloak and your app share the same domain (or use localhost for both).
Cause 2: Reverse Proxy Misconfiguration
Running Keycloak behind a reverse proxy (Nginx, Traefik, Apache, HAProxy) is common in production, but incorrect proxy settings cause login loops by breaking session cookies, mangling redirect URLs, or losing headers.
The Problem
The proxy may:
- Strip the
X-Forwarded-*headers that Keycloak needs - Not forward the
Hostheader correctly - Terminate SSL but not tell Keycloak about it
- Buffer or modify the redirect response
Diagnosis
Check what headers reach Keycloak:
# Watch Keycloak's logs for redirect URLs
docker logs keycloak 2>&1 | grep -i "redirect"
Check the redirect URL Keycloak generates. If it contains http:// when it should be https://, or if the hostname is wrong, the proxy is not forwarding headers correctly.
Fix: Nginx
server {
listen 443 ssl;
server_name auth.example.com;
ssl_certificate /etc/ssl/certs/auth.example.com.crt;
ssl_certificate_key /etc/ssl/private/auth.example.com.key;
location / {
proxy_pass http://keycloak:8080;
# Critical headers for Keycloak
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Do not buffer responses
proxy_buffering off;
# Pass cookies through
proxy_pass_header Set-Cookie;
}
}
Fix: Traefik
# docker-compose.yml with Traefik labels
services:
keycloak:
image: quay.io/keycloak/keycloak:26.0
command: start
environment:
KC_PROXY_HEADERS: "xforwarded"
KC_HTTP_ENABLED: "true"
KC_HOSTNAME_STRICT: "false"
KC_HOSTNAME: "auth.example.com"
labels:
- "traefik.enable=true"
- "traefik.http.routers.keycloak.rule=Host(`auth.example.com`)"
- "traefik.http.routers.keycloak.entrypoints=websecure"
- "traefik.http.routers.keycloak.tls=true"
- "traefik.http.services.keycloak.loadbalancer.server.port=8080"
Fix: Keycloak Proxy Configuration
Since Keycloak 25+, configure proxy headers in Keycloak itself:
# Environment variables
KC_PROXY_HEADERS=xforwarded
KC_HTTP_ENABLED=true
KC_HOSTNAME=auth.example.com
Or in conf/keycloak.conf:
proxy-headers=xforwarded
http-enabled=true
hostname=auth.example.com
Refer to the Keycloak reverse proxy documentation for version-specific configuration.
Cause 3: Hostname Mismatch
Keycloak uses the hostname to generate redirect URLs, token issuer values, and well-known endpoints. If the hostname Keycloak thinks it is serving does not match what the client expects, authentication breaks.
The Problem
This typically happens when:
- Keycloak is configured with
hostname=keycloak(Docker service name) but accessed viaauth.example.com - The issuer in the token (
issclaim) does not match the issuer your application validates against - Keycloak generates redirect URLs with the wrong hostname
Diagnosis
- Check the
issclaim in your tokens using the JWT Token Analyzer - Compare it with the issuer URL your application expects
- Check Keycloak’s
.well-known/openid-configurationendpoint:
curl http://your-keycloak-url/realms/your-realm/.well-known/openid-configuration | jq '.issuer'
Fix
Set the hostname explicitly in Keycloak:
KC_HOSTNAME=auth.example.com
KC_HOSTNAME_STRICT=true
For development environments where you access Keycloak via different URLs:
KC_HOSTNAME_STRICT=false
Make sure your application’s OIDC configuration uses the same base URL that Keycloak reports in its .well-known/openid-configuration.
Cause 4: Multiple Keycloak Instances Without Sticky Sessions
When running Keycloak in a cluster, the authorization code exchange (step 5 in the login flow) must reach the same Keycloak instance that issued the code. If requests are load-balanced randomly, the code exchange will fail.
The Problem
Keycloak stores temporary authentication session data in memory (Infinispan cache). During the OIDC flow, the authorization code is tied to the session on the specific Keycloak node. If the token exchange request hits a different node, it cannot find the session.
Diagnosis
- Check if you have multiple Keycloak instances behind a load balancer
- Look for errors in Keycloak logs about invalid or expired codes
- The issue is intermittent (works sometimes when you happen to hit the right node)
Fix
Option 1: Enable sticky sessions
Configure your load balancer to route requests from the same client to the same Keycloak instance. Keycloak sets an AUTH_SESSION_ID cookie for this purpose.
For Nginx:
upstream keycloak {
ip_hash; # Sticky sessions based on client IP
server keycloak-1:8080;
server keycloak-2:8080;
}
Or use cookie-based sticky sessions:
upstream keycloak {
server keycloak-1:8080;
server keycloak-2:8080;
sticky cookie srv_id expires=1h path=/;
}
Option 2: Configure distributed caching
Ensure Keycloak’s Infinispan caches are configured for distribution across nodes. In a Kubernetes deployment, configure the JGroups discovery protocol:
KC_CACHE=ispn
KC_CACHE_STACK=kubernetes
Cause 5: Cookie Domain and Path Issues
If your application sets cookies on the wrong domain or path, they will not be sent on subsequent requests.
The Problem
- Cookie domain does not match the application domain
- Cookie path is set to a subpath, but the application serves pages outside that path
- Application is on a subdomain and the cookie domain does not include the parent domain
Diagnosis
In browser developer tools, check the cookie attributes:
- Domain: Should match or be a parent of the application domain
- Path: Should be
/for application-wide session cookies - Secure: Must be
truefor HTTPS sites - HttpOnly: Should be
truefor session cookies
Fix
Set explicit cookie domain and path:
// Express.js
app.use(session({
cookie: {
domain: '.example.com', // Include leading dot for subdomain matching
path: '/',
secure: true,
httpOnly: true,
sameSite: 'lax',
},
}));
For Keycloak’s own cookies, check the realm settings under Realm Settings > Sessions. Keycloak manages its own cookies, and these are generally not the problem. The issue is usually with your application’s session cookie.
Cause 6: CORS Configuration
CORS issues during the token exchange can prevent your application from completing the login flow.
The Problem
The authorization code exchange is a back-channel request from your application to Keycloak’s token endpoint. If this is a browser-based request (e.g., from a single-page application), CORS must be configured correctly.
Diagnosis
Check the browser console for CORS errors. The token exchange request will show as failed if CORS headers are missing.
Fix
- In Keycloak, go to Clients > your client > Settings
- Set Web origins to your application’s origin (e.g.,
http://localhost:3000)
For a thorough walkthrough, see our post on configuring CORS with Keycloak OIDC clients.
Note: For server-side applications (Node.js, Spring Boot, Django), the token exchange is a server-to-server request and CORS does not apply. This cause only affects SPAs that make the token exchange from the browser.
Cause 7: Browser Extensions and Privacy Settings
Some browser extensions and privacy settings can interfere with the login flow.
The Problem
- Ad blockers may block requests to certain domains
- Privacy extensions (Privacy Badger, uBlock Origin) may strip cookies or headers
- Firefox Enhanced Tracking Protection can isolate cookies per domain
- Safari ITP (Intelligent Tracking Prevention) blocks third-party cookies aggressively
Diagnosis
- Try the login flow in an incognito/private window with all extensions disabled
- If it works in incognito, an extension or browser setting is the cause
Fix
- Test in a clean browser profile
- If you identify a specific extension, add an exception for your application and Keycloak domains
- For Safari ITP, ensure your application and Keycloak share the same registrable domain, or use
SameSite=None; Secure; Partitionedcookies - Document browser requirements for your users
Cause 8: Redirect URI Mismatch After Login
Sometimes the login succeeds at Keycloak, but the redirect back to your application hits a URL that your application does not handle correctly, starting the loop.
The Problem
- The redirect URI has a trailing slash mismatch
- The redirect URI includes query parameters that your application does not expect
- The application’s callback route is not properly configured
Diagnosis
Watch the network requests in browser developer tools:
- Note the redirect URL Keycloak sends (in the
Locationheader of the 302 response) - Check if your application handles that exact URL
- Look for additional redirects your application makes after receiving the callback
Fix
Ensure your application’s callback handler:
- Matches the exact redirect URI registered in Keycloak
- Processes the authorization code before checking authentication state
- Does not trigger another auth check before the session is established
// Express.js example - handle callback before auth middleware
app.get('/callback', async (req, res) => {
const { code } = req.query;
if (!code) {
return res.redirect('/login');
}
// Exchange code for tokens and create session FIRST
const tokens = await exchangeCode(code);
req.session.accessToken = tokens.access_token;
// THEN redirect to the protected page
res.redirect(req.session.returnTo || '/');
});
// Auth middleware should exclude the callback route
app.use((req, res, next) => {
if (req.path === '/callback') return next();
if (!req.session.accessToken) {
req.session.returnTo = req.originalUrl;
return res.redirect('/login');
}
next();
});
Cause 9: Keycloak Session Timeout
If Keycloak’s SSO session has expired but your application still has a valid session cookie, attempting to silently renew the token can cause a loop.
The Problem
The application tries to silently renew the token (via iframe or back-channel), Keycloak reports the session has expired, and the application triggers a full login redirect, which may succeed and then expire again if session timeouts are too short.
Diagnosis
Check your session timeout settings in Keycloak:
- Go to Realm Settings > Sessions
- Check SSO Session Idle and SSO Session Max
Fix
Align your session timeouts:
SSO Session Idle: 30 minutes (minimum for active users)
SSO Session Max: 10 hours (maximum session duration)
Access Token Lifespan: 5 minutes
Your application should handle the “session expired” response gracefully:
// Handle silent check-sso failure
keycloak.init({
onLoad: 'check-sso',
silentCheckSsoFallback: false, // Do not auto-redirect on failure
}).then(authenticated => {
if (!authenticated) {
// Show login button instead of auto-redirecting
showLoginButton();
}
});
For proper session configuration, see our session management guide.
Systematic Debugging Approach
When you encounter a login loop, follow this sequence:
Step 1: Check Browser Developer Tools
Open the Network tab and reproduce the loop. Look for:
- The redirect chain (each 302 response and where it goes)
- Cookie headers (are cookies being set? are they being sent?)
- CORS errors in the Console tab
- The exact redirect URIs being used
Step 2: Try a Different Browser
Open an incognito window or a different browser. If the loop does not happen, it is browser-specific (extensions, settings, cached cookies).
Step 3: Check Keycloak Logs
docker logs keycloak 2>&1 | tail -100
Look for:
- “Invalid redirect_uri” errors
- “Code not valid” errors
- Session-related warnings
Step 4: Verify Token Exchange
Test the token exchange manually:
# Get the code from the redirect URL, then exchange it
curl -X POST http://localhost:8080/realms/myrealm/protocol/openid-connect/token
-d "grant_type=authorization_code"
-d "code=YOUR_CODE_HERE"
-d "client_id=my-client"
-d "client_secret=my-secret"
-d "redirect_uri=http://localhost:3000/callback"
If this works but the application flow does not, the issue is in your application’s callback handling.
Step 5: Check Event Logs
Enable Keycloak audit logs (Realm Settings > Events) and check the Events section after reproducing the issue. Look for LOGIN_ERROR, CODE_TO_TOKEN_ERROR, or REFRESH_TOKEN_ERROR events.
Prevention
- Use HTTPS everywhere: It eliminates most cookie-related issues.
- Test with multiple browsers: Catch browser-specific issues early.
- Set explicit cookie attributes: Do not rely on browser defaults.
- Monitor auth events: Use Skycloak’s insights to detect authentication failures.
- Keep Keycloak updated: Cookie and security behavior evolves with browser updates. Skycloak handles updates automatically.
- Use the Keycloak Config Generator: Generate properly configured realm settings to avoid common misconfigurations.
Related Resources
- Keycloak 403 Forbidden troubleshooting
- Configuring CORS with Keycloak OIDC clients
- 8 default configurations to adjust on your Keycloak cluster
- Keycloak reverse proxy guide (official docs)
- Session management features
- Security practices
Try Skycloak
Login loops are often caused by infrastructure misconfigurations. Skycloak handles proxy settings, SSL, and Keycloak configuration so you do not have to debug these issues yourself. See our pricing or check the documentation to get started.
Ready to simplify your authentication?
Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.