Authentication · intermediate

Authentication cookie debugging checklist

A focused checklist for engineers when the browser fails to persist or send the session cookie required by the server. The guide sequences DevTools observation, Set-Cookie attribute analysis, storage boundary inspection, and request/response correlation so each competing cause—SameSite, Secure, Path/Domain, expiration, size, and client blocking—can be ruled in or out with named evidence.

The symptoms

  • After a successful POST to the login endpoint, subsequent requests to authenticated routes return 401, 302 to /login, or 403 even though the response body indicates a session was issued.
  • The browser DevTools Application → Cookies view does not show the expected session cookie for the application origin, while the Set-Cookie header is clearly present in the login response.
  • The session cookie appears in storage but the request headers on protected endpoints omit Cookie, often only on cross-site navigations or on requests the browser classifies as third-party.
  • Cookies work in one browser profile or environment and silently fail in another, typically differing by HTTPS, embedded context (iframe), or browser-level cookie blocking settings.

Likely causes

  • Set-Cookie attributes (Secure, SameSite, Domain, Path, HttpOnly, Expires/Max-Age) are inconsistent with the request context, causing the browser to discard or refuse to send the cookie.
  • The application origin sets a cookie with a Domain attribute that does not match the current host, or with a Path that does not include the protected endpoint.
  • The browser's cookie jar is full, blocked for the site, or partitioned (e.g., third-party cookie restrictions) so the cookie is never stored or never attached to the target request.
  • An intermediary (proxy, CDN, WAF, or reverse proxy) rewrites, strips, or duplicates Set-Cookie, or downgrades HTTPS to HTTP on a redirect, invalidating the Secure attribute.
  • JavaScript on the response path throws before document.cookie reads complete, or the client uses fetch with credentials omitted, so a non-HttpOnly cookie set via JS is never persisted.

First ten minutes

  1. 01Open DevTools → Network, enable "Preserve log", and reproduce the failing flow once. Note the login request's status code and the full Set-Cookie response header value.
  2. 02Switch to DevTools → Application → Cookies for the exact origin (scheme + host + port) and confirm whether the session cookie is present, and with what Name, Domain, Path, Expires, and Size.
  3. 03On the next protected request in the Network panel, inspect the request headers for the literal Cookie header and compare its name=value pairs against the cookie jar entry.
  4. 04Read the Application → Storage → Cookies section's per-cookie flags (HttpOnly, Secure, SameSite) and any warning icon; these are the browser's own rejection record when the cookie is not stored.
  5. 05Disable extensions and re-test in a clean profile to rule out content blockers, privacy modes, and tracking protection that silently drop Set-Cookie.
  6. 06Capture a HAR export of one failing flow so the Set-Cookie and Cookie headers for every request can be reviewed offline and diffed across environments.

Evidence to collect

  • Login response status code and the verbatim Set-Cookie header line(s), including all attributes as the browser parsed them.
  • The Application → Cookies view for the login origin and for any redirected origin, showing Name, Value (truncated), Domain, Path, Expires, Size, HttpOnly, Secure, SameSite, and SameParty.
  • The Cookie request header on a representative protected request that should carry the session, captured before any client-side rewrite.
  • Console messages around the login and subsequent request, including any warnings about cookie storage, mixed content, or SameSite context.
  • Network panel timing/flags showing whether the protected request was treated as same-site, cross-site, or as a navigation vs. subresource.

Where to look

  • DevTools → Network: response headers on the login endpoint and request headers on the first protected endpoint after login.
  • DevTools → Application → Cookies, scoped to the exact origin used to log in (scheme + host + port), and to any origin reached via redirect.
  • DevTools → Application → Storage overview to confirm cookies are not disabled at the browser level and that quota or partitioning state is healthy.
  • DevTools → Console and Issues panel for browser-emitted cookie warnings (e.g., SameSite=None missing Secure, partition key requirements, mixed-content rejections).
  • Server access log for the protected endpoint to confirm whether a session identifier reached the application at all, independent of the browser.

Diagnostic steps

  1. 01Compare the Set-Cookie Domain and Path attributes against the protected endpoint's host and path prefix; a mismatch is a sufficient cause for the cookie not being attached.
  2. 02Check the Secure attribute against the request scheme: a Secure cookie set over HTTP is discarded, and a Secure cookie set on a non-secure redirect chain may be dropped depending on browser policy.
  3. 03Determine the request context for the protected call: same-site navigation, cross-site navigation, subresource fetch, or iframe. Map this to the cookie's SameSite value (Strict/Lax/None) and decide whether the browser's default Lax behavior is suppressing it.
  4. 04Verify HttpOnly: if the cookie is HttpOnly, JavaScript reads of document.cookie will return nothing—this is expected and not a bug, but it rules out JS-based session attachment as the cause.
  5. 05Check the cookie's Expires/Max-Age against the time elapsed; a session cookie without Expires persists only for the browser session and is lost on restart.
  6. 06Inspect cookie size and total jar size against the browser's per-cookie and per-domain limits; oversized values are silently truncated or rejected.
  7. 07Trace a single failing request through any reverse proxy, CDN, or WAF to confirm Set-Cookie is not being stripped, normalized, or duplicated, and that HTTPS is not downgraded mid-redirect.
  8. 08Reproduce in a clean browser profile with extensions disabled to isolate third-party blocking, tracking protection, and partitioned storage as the cause.

Common mistakes

  • Assuming the login response sets a session cookie without reading the literal Set-Cookie header; some flows rely on a separate token endpoint, an OAuth callback, or a non-cookie bearer token.
  • Reading document.cookie to verify a session exists; HttpOnly cookies are intentionally invisible to JavaScript, and their absence there is not evidence of failure.
  • Setting SameSite=None without Secure, which causes modern browsers to reject the cookie outright rather than send it.
  • Configuring Domain too broadly (e.g., parent domain) so the cookie is shared with subdomains that should not receive it, or too narrowly so it never reaches the protected host.
  • Trusting the Application → Cookies view on a different origin than the one that issued the cookie; cookies are origin-scoped and the view filters accordingly.
  • Conflating "no Set-Cookie in the response" with "browser refused to store a cookie"; these require different evidence (raw response header vs. browser's cookie jar).

Safe fixes

  • If the cookie is missing from the jar but Set-Cookie is present: adjust attributes on the server response to match the request scheme and context—Secure for HTTPS, SameSite appropriate to the cross-site pattern, Domain and Path matching the protected endpoint.
  • If the cookie is stored but never sent on protected requests: change the request to be same-site, or set SameSite=None; Secure so it is permitted cross-site, and confirm fetch/XHR calls include credentials.
  • If the cookie is stored and sent but the server still rejects the session: verify the server reads the cookie name it issues, that the signing key or session store is the same instance across requests, and that no upstream proxy strips the Cookie header.
  • If size limits are suspected: reduce the cookie payload or move opaque data server-side, keeping the cookie within the browser's per-cookie and per-domain limits.
  • Only after all of the above have been verified should cookie-related code paths, interceptor logic, or session middleware be modified; coordinate changes with the server team to avoid divergent environments.

Prove the fix

  1. 01In a clean browser profile, perform the full login flow once and confirm the session cookie is present in DevTools → Application → Cookies for the login origin, with the expected Name, Domain, Path, and flags.
  2. 02Trigger one authenticated request via direct navigation to a protected URL and verify the Network panel shows a Cookie header containing the session name=value pair, with a 2xx or expected 3xx response.
  3. 03Trigger one cross-site or subresource case that previously failed and confirm the Cookie header is now attached and the server returns the expected authenticated response.
  4. 04Close and reopen the browser (preserving the session cookie's expiration) and re-attempt the protected request to confirm the cookie survived restart when Expires/Max-Age is set.
  5. 05Export a HAR of the fixed flow and diff it against the previously failing HAR to demonstrate the Set-Cookie attributes and the resulting Cookie request headers are now consistent.

Prevention and next steps

  • Treat cookie attributes (Secure, HttpOnly, SameSite, Domain, Path, Expires/Max-Age) as a contract owned alongside the session schema; review changes in code review and document the expected request contexts.
  • Add automated browser tests that assert both the Set-Cookie response attributes and the Cookie request header on the first protected request after login, across same-site and cross-site scenarios.
  • Centralize session cookie configuration in a single server module so that intermediaries (proxy, CDN, WAF) cannot silently rewrite it; pin the configuration per environment.
  • Monitor server logs for a sudden rise in 401/302-to-login responses on protected endpoints as an early indicator of cookie persistence or attachment regressions.
  • Document the browser matrix and the supported SameSite/Secure combinations so client and server teams share a single source of truth for cookie semantics.

Safe commands and checks

awk 'tolower($0) ~ /set-cookie:/ {print}' <login_response.har>  # extract Set-Cookie lines from a HAR export of the login response
awk 'tolower($0) ~ /^cookie:/ {print}' <protected_request.har>  # extract the Cookie request header from a HAR export of a protected request
grep -nE '"name":"(Set-Cookie|Cookie)"' <session.har>  # locate Set-Cookie/Cookie entries by JSON name in a HAR file for structured review
grep -nE 'session|sid|auth' <auth_service_access.log>  # inspect server access log lines mentioning session identifiers for the protected endpoint