Keycloak Login Loop: How to Fix Infinite Redirect Issues

Guilliano Molaire Guilliano Molaire Updated April 6, 2026 10 min read

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:

  1. User visits a protected page in your application
  2. Application redirects to Keycloak’s authorization endpoint
  3. User authenticates in Keycloak (enters credentials)
  4. Keycloak redirects back to your application with an authorization code
  5. Application exchanges the code for tokens (via back-channel)
  6. Application creates a session (sets a cookie)
  7. 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; Secure set

Diagnosis

Open your browser developer tools:

  1. Go to the Application tab (Chrome) or Storage tab (Firefox)
  2. Look at Cookies for your application domain
  3. Check the SameSite attribute of the session cookie
  4. Go to the Network tab, find the redirect request from Keycloak to your app, and check if the cookie is being sent (look for Cookie header)

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 Host header 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 via auth.example.com
  • The issuer in the token (iss claim) does not match the issuer your application validates against
  • Keycloak generates redirect URLs with the wrong hostname

Diagnosis

  1. Check the iss claim in your tokens using the JWT Token Analyzer
  2. Compare it with the issuer URL your application expects
  3. Check Keycloak’s .well-known/openid-configuration endpoint:
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 true for HTTPS sites
  • HttpOnly: Should be true for 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

  1. In Keycloak, go to Clients > your client > Settings
  2. 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

  1. Try the login flow in an incognito/private window with all extensions disabled
  2. 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; Partitioned cookies
  • 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:

  1. Note the redirect URL Keycloak sends (in the Location header of the 302 response)
  2. Check if your application handles that exact URL
  3. Look for additional redirects your application makes after receiving the callback

Fix

Ensure your application’s callback handler:

  1. Matches the exact redirect URI registered in Keycloak
  2. Processes the authorization code before checking authentication state
  3. 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:

  1. Go to Realm Settings > Sessions
  2. 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

  1. Use HTTPS everywhere: It eliminates most cookie-related issues.
  2. Test with multiple browsers: Catch browser-specific issues early.
  3. Set explicit cookie attributes: Do not rely on browser defaults.
  4. Monitor auth events: Use Skycloak’s insights to detect authentication failures.
  5. Keep Keycloak updated: Cookie and security behavior evolves with browser updates. Skycloak handles updates automatically.
  6. Use the Keycloak Config Generator: Generate properly configured realm settings to avoid common misconfigurations.

Related Resources

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.

Guilliano Molaire
Written by Guilliano Molaire Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Ready to simplify your authentication?

Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.

© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman