"The request works in Postman but the browser blocks it" is CORS; "the cookie is set but not sent" is SameSite; "the user is logged in on the server render but not after hydration" is credentials during SSR. These three come up in almost every fullstack interview because every team has lost a day to them.
fetch semantics: the promise resolves as soon as headers arrive and does not reject on 4xx/5xx (check response.ok/status); it rejects on network failure, CORS blocks and aborts. Bodies are read once (json(), text(), formData(), arrayBuffer(), or body as a stream). Options: method, headers, body, signal (AbortController), credentials (same-origin default, include, omit), mode (cors default, same-origin, no-cors → opaque response), cache (default, no-store, reload, no-cache, force-cache), redirect, keepalive (outlives the page, for beacons, 64 KB limit), priority. ofetch (Nuxt's $fetch) adds JSON handling, throwing on error status, retries and interceptors.<img>, <script>, <iframe>) is allowed; reading is not.Origin; the server answers Access-Control-Allow-Origin (a specific origin, or * which is incompatible with credentials), Access-Control-Allow-Credentials: true to allow cookies, Access-Control-Expose-Headers for non-safelisted response headers. Preflight (OPTIONS with Access-Control-Request-Method/-Headers) happens for non-simple requests: methods other than GET/HEAD/POST, custom headers such as Authorization, or Content-Type: application/json (only form types are simple); Access-Control-Allow-Methods/-Headers and Access-Control-Max-Age answer it. Redirects and missing Vary: Origin are classic causes of intermittent failures.mode: 'no-cors' yields an opaque response you cannot read and is rarely what you want.Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy and Cross-Origin-Resource-Policy isolate a page from other origins (required for SharedArrayBuffer and high-resolution timers); the crossorigin attribute on <script>/<img> requests CORS mode so error details and canvas reads are allowed; integrity adds subresource integrity.Set-Cookie: name=value; Max-Age=...; Path=/; Domain=...; Secure; HttpOnly; SameSite=Lax. Secure: HTTPS only. HttpOnly: invisible to document.cookie (session cookies always). SameSite: Strict (never on cross-site requests, including top-level navigation from a link), Lax (default in Chromium; sent on top-level GET navigations, not on cross-site POST or subresources), None (cross-site allowed; requires Secure). Prefixes: __Host- (Secure, no Domain, Path=/) and __Secure-. Partitioned (CHIPS) for third-party cookies keyed by top-level site. Limits: ~4 KB each, a few hundred per domain, sent on every matching request (keep them small). Expires/Max-Age for persistence; session cookies without them.SameSite=Lax/Strict mitigates most of it, and token or origin-header checks close the rest (the security section covers it).useCookie(name, options) reads and writes on both sides; on the server getCookie/setCookie/deleteCookie from h3; SSR fetches do not carry the browser's cookies unless you forward them (useRequestFetch(), useRequestHeaders(['cookie'])); the Cookie Store API (cookieStore) enables change events, which Nuxt's experimental.cookieStore uses to keep useCookie refs in sync.navigator.sendBeacon() or fetch(url, { keepalive: true }) from visibilitychange/pagehide, never unload (which also breaks bfcache).Reading a CORS failure from the wire:
OPTIONS /api/orders HTTP/1.1
Origin: https://shop.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://shop.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PATCH, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 600
Vary: Origin
Missing Vary: Origin with several allowed origins means a CDN can cache the response for origin A and serve it to origin B, which then fails. Allow-Origin: * with credentials: 'include' fails outright.
CORS in Nitro for a public API, and cookie handling on both sides of a Nuxt app:
export default defineNuxtConfig({
routeRules: {
'/api/public/**': { cors: true, headers: { 'access-control-allow-origin': 'https://partner.example.com', 'access-control-allow-credentials': 'true', 'vary': 'Origin' } },
},
})
export default defineEventHandler(async (event) => {
const user = await authenticate(await readBody(event))
setCookie(event, '__Host-session', await seal(user), {
httpOnly: true, // invisible to scripts: XSS cannot read it
secure: true, // HTTPS only (required by the __Host- prefix)
sameSite: 'lax', // sent on top-level navigations, not on cross-site POSTs
path: '/',
maxAge: 60 * 60 * 24 * 7,
})
return { ok: true }
})
// Forward the incoming cookie during SSR so the API sees the same session as the page.
export function useApi() {
const headers = import.meta.server ? useRequestHeaders(['cookie']) : undefined
return $fetch.create({ baseURL: '/api', headers, credentials: 'include' })
}
// A preference cookie the server can read for the first render; not HttpOnly because the client writes it.
export const useConsent = () => useCookie<'granted' | 'denied' | null>('consent', {
default: () => null,
sameSite: 'lax',
secure: true,
maxAge: 60 * 60 * 24 * 180,
})
Beacons that survive navigation without blocking bfcache:
export default defineNuxtPlugin(() => {
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
navigator.sendBeacon('/api/telemetry/session', JSON.stringify({ path: location.pathname, at: Date.now() }))
}
})
})
await fetch(url).then(r => r.json()) happily parses an error page as JSON or throws a confusing parse error. Check response.ok first, or use ofetch, which throws a FetchError with status and data for non-2xx responses.
A user who clicks a link to your app from an email arrives without their session cookie (top-level cross-site navigation) and sees the logged-out state. Lax sends the cookie on such GET navigations while still blocking cross-site POSTs; Strict is for cookies that only matter after the user is already inside.
The current status of third-party cookies in Chrome (user-choice model), Safari and Firefox, and CHIPS (Partitioned) support, as of the interview week.
localhost:3111, fetch an API on another port with Content-Type: application/json; read the preflight in the Network panel, then make it a simple request and watch the preflight disappear.SameSite=Strict, open the app from a link in a different site (or a file:// page), and observe the logged-out render; switch to Lax.useApi and diff the server-rendered HTML against the hydrated DOM for a logged-in user."The same-origin policy: the browser lets a page send cross-origin requests but refuses to hand the response to the script unless the server opts in. For anything beyond a simple GET or form POST, the browser first sends an OPTIONS preflight with the origin, method and headers it wants to use, and the server must answer with Access-Control-Allow-Origin for that origin, plus allowed methods and headers, and Allow-Credentials if cookies are involved, which also rules out the wildcard origin. Curl skips all of this because there is no origin to protect. So I look at the preflight in the Network panel, fix the server's allow headers, add Vary: Origin if several origins are allowed, and remember that CORS protects the user's browser data, not my API: authentication and CSRF protection are separate concerns."
"HttpOnly so scripts cannot read it and an XSS cannot exfiltrate the session. Secure so it never travels over plain HTTP, and the __Host- prefix to lock it to this host with Path=/ and no Domain. SameSite=Lax so it is sent on top-level navigations, which keeps links from emails working, but not on cross-site POSTs or subresources, which removes most CSRF; Strict only for cookies that matter after the user is inside. A Max-Age matched to the session policy, and a small sealed value rather than data, because every cookie rides on every request. In Nuxt the server sets it with h3's setCookie and the page reads the user through the session API, never through the cookie itself."
Nothing yet. Notes filed here show up on this page and the section overview.
HTTP and caching
HTTP/1.1 to HTTP/3, methods and status codes worth knowing, and caching as the biggest lever you control — Cache-Control directives, validators and 304s, Vary, CDN behaviour, immutable hashed assets, stale-while-revalidate, resource hints, early hints and speculation rules.
Storage and offline
The browser storage options and their quotas, eviction and SSR constraints — Web Storage, IndexedDB, the Cache API, OPFS — plus service workers and their update lifecycle, PWA installability, Web Workers and cross-tab communication, and what "offline-first" actually commits you to.