Cheat sheet · The Browser Contract: Auth, Storage, Security & HTTP
The Browser Contract cheat sheet
What the browser does between Enter and a usable page, and the rules you need in an interview: storage, auth, CORS, caching, HTTP codes, security and speed. Always break time down by layer and use numbers.
blogs · jp-blogs.vercel.app/cheatsheets/series/browser
Enter to pixels: 11 stepsLesson 0 →
- Input: URL or search? Normalise it (add scheme, lowercase host).
- Shortcuts: HSTS, Service Worker, HTTP cache, bfcache.
- DNS: browser, OS, resolver, then root, TLD, authoritative.
- TCP handshake (1 RTT) or QUIC for HTTP/3.
- TLS 1.3: 1 RTT, certificate checked.
- Request with cookies, then server work (CDN, app, DB). Time to first byte is TTFB.
- Parse HTML to DOM and CSS to CSSOM. Style, layout, paint, composite.
- After load: hydration, lazy resources, API calls. Painted is not interactive.
Tip: Cold HTTPS over TCP is DNS + 1 RTT + 1 RTT before the first request byte. At 250 ms RTT that is 500 ms or more.
What skips workLesson 0 →
| Cache | Skips |
|---|---|
| bfcache | Everything (whole page from memory) |
| Service Worker | The network |
| HTTP cache, fresh | DNS, TCP, TLS, request, server |
HTTP cache, 304 | Only the body download |
| Connection reuse (H2/H3) | TCP and TLS handshakes |
| CDN / Redis | Origin work / database work |
| Script tag | Blocks parsing? | Order kept? |
|---|---|---|
Plain script | Yes | Yes |
async | Only while running | No |
defer / type=module | No (runs after parse) | Yes |
dns-prefetchlooks up,preconnectopens DNS+TCP+TLS,preloadfetches one file.- CSS blocks rendering. Plain JS blocks parsing. HTTP/3 removes TCP head-of-line blocking.
Five browser storesLesson 1 →
| Store | Lives | Size | Sent to server? |
|---|---|---|---|
| Cookie | Until Max-Age/Expires | ~4 KB each | Yes, every request |
localStorage | Until cleared | ~5 MB | No |
sessionStorage | Until the tab closes | ~5 MB | No |
| IndexedDB | Until cleared | Large, async | No |
| Cache API | Until evicted | Large, async | No |
localStorage: sync, strings only, shared by every script on the origin,storageevent in other tabs.- Storage can vanish (Safari ~7 days idle, low space). Wrap calls in
try/catch. Keep the real copy on the server. - Secrets go nowhere in the browser. Theme the server needs: cookie. Big or binary data: IndexedDB.
Cookie attributesLesson 1 →
- HttpOnly
- JS cannot read it (stops theft, not use)
- Secure
- Send over HTTPS only
- SameSite
- Lax, Strict or None.
NoneneedsSecure. Set it yourself - Domain
- Omit = exact host. Set = all subdomains (riskier)
- Path
- Convenience only, not a security wall
- __Host- prefix
- Forces
Secure,Path=/, noDomain - Partitioned
- Third-party cookie kept per top-level site
Set-Cookie: __Host-sid=abc; Path=/; Secure;
HttpOnly; SameSite=Lax; Max-Age=3600A good session cookie.
Session vs JWT and claimsLesson 2 →
| Aspect | Server session | JWT |
|---|---|---|
| Data lives | Server (Redis/DB) | In the token |
| Per request | Store lookup | Signature check, no I/O |
| Logout / revoke | Easy: delete record | Hard until exp |
| Role change | Next request | After expiry or refresh |
| Best for | First-party web apps | APIs, mobile, many services |
- iss / aud
- Issuer you expect / token is for your API
- sub
- The user
- exp / nbf / iat
- Expiry / not before / issued at (Unix seconds)
- jti
- Token ID, for a deny list
- Pin
algorithmswhen verifying. Defeatsalg: noneand RS256-to-HS256 confusion. decodeonly reads.verifychecks signature and claims. Treatkidas untrusted input.- HS256 = one shared secret (verifier can forge). RS256/ES256 = private signs, public verifies (JWKS).
- Access token 5 to 15 min. Refresh token in HttpOnly cookie, recorded on the server, rotated on each use.
- Reused old refresh token means theft: revoke the whole family. "Log out everywhere": bump
tokenVersion.
Watch out: A signed JWT (JWS) is not encrypted. Anyone can base64-decode the payload. Never put secrets in it.
XSS vs CSRF and token homesLesson 3 →
| Where | XSS steals it? | CSRF? |
|---|---|---|
localStorage token | Yes | No |
| Non-HttpOnly cookie | Yes | Yes |
| HttpOnly cookie | No, but XSS can still use it | Yes, defend |
| JS memory | Harder | No |
- Strong defaults: HttpOnly session cookie; access token in memory + HttpOnly refresh cookie; or a BFF (no tokens in the browser).
- CSRF layers:
SameSite, CSRF token,Origincheck,Sec-Fetch-Site, custom header, never change data onGET. Laxfails against sibling subdomains (same site, different origin). JWT in a cookie still needs CSRF defence.
CORSLesson 4 →
- Origin = scheme + host + port. CORS lets a server allow other origins to read responses. It is browser-only.
- Simple request:
GET/HEAD/POSTwith form-typeContent-Type. JSON,AuthorizationorPUT/DELETEtrigger anOPTIONSpreflight. - With credentials:
credentials: "include", exact origin (no*),Allow-Credentials: true,Vary: Origin.
Allow-Origin: https://app.example.com
Allow-Methods: GET, POST, PUT
Allow-Headers: content-type, authorization
Allow-Credentials: true
Max-Age: 600
Vary: OriginPreflight reply (204). Each name starts with Access-Control-.
Watch out: CORS is not API security and not CSRF protection. Put CORS middleware before auth, or the preflight gets 401. A 5xx without headers shows as a CORS error.
Cache-Control directivesLesson 5 →
- max-age=N
- Fresh for N seconds, no request
- s-maxage=N
- Shared caches only, overrides
max-age - no-cache
- Store, but revalidate before each use
- no-store
- Never store (sensitive data)
- private / public
- Browser only / any cache
- immutable
- Never changes at this URL
- stale-while-revalidate=N
- Serve old copy while refreshing
- ETag / If-None-Match
- Validator. Unchanged gives
304, no body
Watch out: 304 saves the download but still costs a round trip. No Cache-Control means guessed (heuristic) freshness.
Caching recipe and stale deploysLesson 5 →
| Resource | Header |
|---|---|
Hashed JS/CSS, /_next/static | public, max-age=31536000, immutable |
| HTML | no-cache |
| Public API data | public, s-maxage=60, stale-while-revalidate=300 |
| Per-user data | private, no-cache or no-store |
- Stale after deploy? Check HTML
max-age, CDN copy (Age,x-cache), unhashed names, deleted old chunks, Service Worker, API caching. - Cache-bust by changing the URL (content hash). Send
VaryforAccept-Encoding,Origin.
Methods and status codesLesson 6 →
| Method | Safe | Idempotent |
|---|---|---|
GET / HEAD / OPTIONS | Yes | Yes |
PUT / DELETE | No | Yes |
POST | No | No (use an idempotency key) |
PATCH | No | Depends on the patch |
- 401
- Not logged in (send
WWW-Authenticate) - 403
- Logged in, not allowed
- 409 / 412
- State conflict /
If-Matchfailed - 422 / 400
- Valid format, bad meaning / broken request
- 429
- Rate limited, send
Retry-After - 202
- Accepted, work queued
- 502 / 503 / 504
- Bad upstream / overloaded / upstream timeout
RedirectsLesson 6 →
| Code | Permanent? | Method kept? |
|---|---|---|
| 301 | Yes (cached) | May become GET |
| 302 | No | May become GET |
| 303 | No | Always GET |
| 307 | No | Kept |
| 308 | Yes (cached) | Kept |
Tip: After a form POST use 303 (Post/Redirect/Get). Moved POST API: 307 or 308. Unsure it is permanent: use 302/307.
Frontend security and headersLesson 7 →
- React escapes
{value}. Sinks:dangerouslySetInnerHTML,innerHTML,eval,javascript:inhref,postMessagewithoutevent.origin. - Sanitise HTML with an allow-list (DOMPurify). Allow only
http:/https:URLs. - Authorization lives on the server. Hiding a button is not security.
- Supply chain: lockfile +
npm ci, audit in CI, few deps, no install scripts, SRI on third-party files.
- Strict-Transport-Security
- HTTPS only for a set time (HSTS)
- X-Content-Type-Options: nosniff
- No MIME guessing
- Referrer-Policy
strict-origin-when-cross-origin- Permissions-Policy
- Switch off camera, mic, geolocation
- frame-ancestors / X-Frame-Options
- Stops clickjacking
- COOP / COEP
- Isolate the page (
SharedArrayBuffer)
Content-Security-Policy: default-src 'self';
script-src 'nonce-R4ND0M'
'strict-dynamic';
object-src 'none'; base-uri 'none';
frame-ancestors 'none'Start with Content-Security-Policy-Report-Only.
Rendering and the event loopLesson 8 →
- One main thread runs JS and rendering. Frame budget 16.7 ms (60 Hz). Long task: over 50 ms.
- Loop: one task, then all microtasks, then rAF callbacks, then style, layout, paint.
- Animate
transformandopacity(composite only), nottop,width,height(layout). - Layout thrashing: read (
offsetHeight), write, read... Batch reads first, then writes, in rAF. - Use
{ passive: true },IntersectionObserver,scheduler.yield(), Web Workers,content-visibility: auto.
Tip: Order: sync code, promise/microtask, timeout. A long microtask chain starves rendering. Debug in the Performance panel with 4x CPU throttle.
Core Web VitalsLesson 9 →
| Metric | Good | Poor |
|---|---|---|
| LCP (loading) | ≤ 2.5 s | > 4 s |
| INP (response) | ≤ 200 ms | > 500 ms |
| CLS (stability) | ≤ 0.1 | > 0.25 |
- Judged at the 75th percentile of real users, mobile and desktop apart.
- LCP = TTFB + load delay + load duration + render delay. Hero: img tag,
fetchpriority="high",srcset, never lazy. - INP = input delay + processing + presentation. CLS fix: set
width/heightoraspect-ratio, reserve ad slots. - Field data (CrUX,
web-vitals) says what users feel. Lab (Lighthouse) reproduces. INP replaced FID in March 2024.
Interview one-linersLesson 0 →
| Question | Short answer |
|---|---|
no-cache vs no-store? | Revalidate before use vs never store. |
| Does HttpOnly stop XSS? | No. It stops cookie theft, not use of the session. |
| Is CORS a firewall? | No. Browser-only; it blocks reading the response, not the request. |
| 401 vs 403? | Not logged in vs logged in but not allowed. |
| JWT logout? | Drop client token, revoke refresh token, short exp, deny list or version. |
| Why INP over FID? | It covers all interactions and the full delay. |
| Why does the Postman call work? | Postman does not enforce CORS. |
A cheat sheet is a reminder, not a teacher. Each card links to the lesson that explains it step by step.