JWT, cookies, CORS and security headers

Tokens and the browser's rules: what is inside a JWT, where to store tokens, what CORS really controls and the headers worth sending.

On the roadmap:Intermediate · step 7: Secure by design

Lesson 4 of 5 · 13 min read · quiz · exercise

Cookies and sessions, quickly

A session keeps a user logged in. The usual approach: after login the server creates a random session id, stores session data server-side, and sends the id in a cookie. (Passwords and sessions are detailed in their own lesson.) Good cookie flags:

FlagEffect
HttpOnlyJavaScript cannot read it, limiting theft through XSS
SecureSent only over HTTPS
SameSite=Lax/StrictLimits cross-site sending, reducing CSRF
Path, DomainLimit where it is sent
Max-Age / ExpiresLifetime; keep session cookies short-lived with renewal

JSON Web Tokens (JWT)

A JWT is a compact, signed token with three dot-separated, base64url parts: header (algorithm), payload (claims) and signature.

Figure SDA-NSD-04-1 · Anatomy and checking of a JWT · © forcsguy.com

Why use them: the API can verify a token without a database lookup, which suits services and mobile apps. The cost: a stateless token is hard to revoke before it expires.

Good practice

  • Keep access tokens short-lived (minutes) and use refresh tokens (stored carefully, rotated, revocable) to get new ones.
  • Always verify signature, exp, iss (issuer) and aud (audience); pin the algorithm.
  • A JWT is signed, not encrypted: do not put sensitive data in it (use JWE if you must encrypt).
  • Where to store in a browser: HttpOnly cookies resist XSS theft but need CSRF protection; localStorage is readable by any script, so an XSS leaks the token. There is no perfect answer: choose with your threat model, and prevent XSS first.
  • For simple server-rendered apps, server-side sessions are often simpler and easier to revoke than JWTs. (Federated login with OAuth and OpenID Connect is in the SSO lesson.)

Same-origin policy and CORS

Browsers enforce the same-origin policy: a page from one origin (scheme, host and port) cannot read responses from another origin. CORS (cross-origin resource sharing) is how a server opts in to being read by specific other origins.

Figure SDA-NSD-04-2 · A cross-origin request with a preflight · © forcsguy.com

Key points:

  • CORS protects users' browsers, not your server. It does not stop curl, scripts or other servers from calling your API; you still need authentication and authorisation.
  • A simple request (GET or simple POST with plain headers) is sent directly; the browser only decides whether the page may read the response. Other requests get a preflight.
  • Configure exact origins, not *, when credentials are involved. Access-Control-Allow-Credentials: true cannot be combined with *. Never blindly echo the Origin header.
  • Limit allowed methods and headers; cache preflights with Access-Control-Max-Age.

Security headers worth sending

HeaderPurpose
Strict-Transport-SecurityTell browsers to use HTTPS only for your domain
Content-Security-PolicyRestrict script, style, frame and connection sources; mitigates XSS and clickjacking
X-Content-Type-Options: nosniffStop browsers guessing content types
Referrer-PolicyLimit referrer information sent to others
Permissions-PolicyTurn off powerful browser features you do not use
Cross-Origin-Opener-Policy / Cross-Origin-Resource-PolicyIsolate your pages from other origins

Caching headers and compression

  • Cache-Control controls caching: public, max-age=31536000, immutable for fingerprinted static files; no-store for sensitive responses; private for per-user responses that shared caches must not store. ETag / Last-Modified allow cheap revalidation (a 304 response with no body).
  • Compression (gzip, Brotli) shrinks text responses a lot. One caution: compressing responses that mix secrets with attacker-controlled text over TLS can enable attacks such as BREACH, so avoid reflecting secrets and use per-request tokens.
  • Do not cache authenticated pages in shared caches by accident; set Vary correctly.

Checklist

Cookies: HttpOnly, Secure, SameSite. Tokens: short-lived, verified fully, no secrets inside. CORS: exact origins, minimal methods. Headers: HSTS, CSP, nosniff, frame protection. Caching: private or no-store for user data.

Real-world reading

Check your understanding

Question 1 of 3

0 correct so far

What does the signature on a JWT guarantee?

Go deeper

Topics covered in this lesson

All topics

© 2026 forcsguy.com. Original lesson text and diagrams. Books and sites linked above belong to their authors.