1. Confirm the browser stored the cookie
Start with the response that creates the cookie. In DevTools, open Network, select the response, and inspectSet-Cookie. Then check the browser's Application or Storage panel. A cookie that never reached the cookie store cannot appear on a later request.
Browsers often show a blocked-reason indicator next to a rejected Set-Cookie header. Common causes include an invalid Domain, SameSite=None without Secure, a malformed attribute, or a third-party cookie policy.
curl -i https://api.example.com/session
HTTP/2 200
Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax2. Match Domain, Path, and Secure to the request URL
For a stored cookie to be sent, every scope rule must match the destination URL:
- Domain: a host-only cookie set by
api.example.comis not sent towww.example.com. A validDomain=example.comcookie can cover subdomains. - Path:
Path=/accountmatches/account/profile, but not/api/profile. - Secure: a Secure cookie is sent only over HTTPS. Browsers may special-case localhost during development, but production behavior should be tested over HTTPS.
Do not add a broad Domain or Path merely to make a test pass. Use the narrowest scope that covers the endpoints that actually need the cookie.
3. Determine whether the request is cross-site
SameSite is based on the registrable site and scheme, not only whether two URLs have different origins.app.example.com and api.example.com are cross-origin but normally same-site; an embedded request from customer.example to api.example.com is cross-site.
| SameSite value | Typical behavior |
|---|---|
Strict | Withheld on cross-site requests, including top-level navigations. |
Lax | Withheld on most cross-site subrequests, but allowed on qualifying top-level navigations. |
None; Secure | Eligible for cross-site use, subject to browser third-party cookie policy. |
4. Include credentials on cross-origin fetch requests
Fetch uses same-origin credentials by default. When the API is on another origin, opt in explicitly:
const response = await fetch('https://api.example.com/me', {
credentials: 'include'
})The API response must also contain compatible CORS headers:
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: OriginCredentialed requests cannot use Access-Control-Allow-Origin: *. The preflight response, when one is required, and the actual response must both satisfy the browser's CORS checks. Use theCORS Debugger or the preflight checklistto isolate that part of the failure.
5. Check lifetime, prefixes, and browser policy
- Confirm
Expiresis in the future andMax-Ageis greater than zero. __Host-cookies require Secure, no Domain attribute, andPath=/.__Secure-cookies require Secure and must be set from a secure page.- Private browsing, storage limits, user settings, and third-party cookie restrictions can reject or partition cookies.
- A proxy or framework can rewrite or remove
Set-Cookie; compare the origin response with what reaches the browser.
Fast diagnosis checklist
- Confirm the cookie was stored: Inspect the Set-Cookie response and the browser cookie store before debugging the next request.
- Check URL scope: Verify that Domain, Path, and Secure match the URL receiving the request.
- Check SameSite context: Determine whether the request is same-site or cross-site and choose an appropriate SameSite value.
- Check fetch and CORS credentials: For cross-origin requests, include credentials on the client and allow credentials for the exact origin on the server.
- Check lifetime and browser policy: Verify expiration, cookie prefixes, storage limits, and third-party cookie restrictions.
Once you identify the mismatched rule, generate a corrected header with theCookie Builder, then verify the result in a fresh browser session.