Keycloak Login Loop: How to Fix Infinite Redirect Issues
Last updated: March 2026
A Keycloak infinite login loop means Keycloak is successfully authenticating the user but your application cannot persist the resulting session, so it immediately redirects back to Keycloak. The most common cause in modern browsers is a session cookie with SameSite=Strict or SameSite=Lax that the browser refuses to send on the cross-site redirect back from Keycloak. The fix is to set SameSite=None; Secure on the session cookie, or to ensure Keycloak and your application share the same registrable domain.
Other frequent causes include a reverse proxy that strips X-Forwarded-* headers, causing Keycloak to generate redirect URLs with the wrong hostname, and a callback handler that triggers an auth check before the session is fully established.
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
Frequently asked questions
Why does Keycloak keep redirecting to the login page after a successful login?
The redirect loop means your application authenticated the user but could not establish a session. The most likely cause is a session cookie being blocked by the browser’s SameSite policy. When Keycloak redirects back to your application, the browser treats it as a cross-site navigation and will not send cookies set with SameSite=Strict or SameSite=Lax. Set the session cookie to SameSite=None; Secure (HTTPS is required) to allow it across the redirect.
How do I fix a Keycloak login loop caused by a reverse proxy?
A misconfigured reverse proxy typically causes Keycloak to generate redirect URLs with the wrong scheme or hostname. Keycloak must receive the X-Forwarded-Proto, X-Forwarded-Host, and X-Forwarded-Port headers from the proxy, and you must set KC_PROXY_HEADERS=xforwarded so Keycloak trusts them. Check the redirect URL in Keycloak’s logs: if it contains http:// when it should be https://, the proxy is not forwarding the protocol header correctly.
How do I debug a Keycloak login loop in a browser?
Open the browser developer tools and switch to the Network tab before reproducing the loop. Look for the chain of 302 redirects and check whether your application’s session cookie is present in the Set-Cookie response headers and then included in the Cookie request headers on subsequent requests. If the cookie is set but not sent, inspect its SameSite, Secure, and Domain attributes. Also try the flow in an incognito window with all extensions disabled to rule out ad blockers or privacy extensions.
Can multiple Keycloak instances cause a login loop?
Yes. When Keycloak runs in a cluster without sticky sessions, the token exchange request may reach a different node than the one that issued the authorization code. Keycloak stores the temporary authentication session in its Infinispan cache, and the second node cannot find the session, causing the code to be rejected. Configure your load balancer for sticky sessions using the AUTH_SESSION_ID cookie, or configure distributed Infinispan caching so all nodes share session state.
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.