Prompt and context
Your site intercepts navigation requests with a Service Worker for offline fallback and shared caching. The first navigation waits for the worker to start before the network request begins. Design navigation preload for unsupported browsers while preventing duplicate requests, stale cache writes, and response races.
What the interviewer is testing
Navigation preload is a browser-started navigation request that runs while the Service Worker starts; it is neither precaching nor link rel=preload for arbitrary resources. Cover enabling it during activate, reading preloadResponse in fetch, network-versus-cache policy, the Service-Worker-Navigation-Preload header, timeout and error fallback, and measurement.
Clarifying questions to ask first
Page and cache target
Clarify whether navigation returns SSR HTML, an SPA shell, or an offline page; which paths are cacheable; and whether user-specific data must bypass shared caches. Personalized responses must not be written to a public cache.
Lifecycle and browser support
Confirm registration, update, control scope, and target browser support for navigationPreload. An uncontrolled first navigation cannot be assumed to pass through the current worker.
Network and consistency policy
Choose network-first, cache-first, or stale-while-revalidate behavior and define the offline response. Decide how the server reads the preload header without accidentally changing cache keys.
30-second answer framework
“Feature-detect and enable navigation preload in the Service Worker’s activate event. The browser starts the navigation request while the worker starts; the fetch handler first awaits event.preloadResponse, then checks cache or calls fetch only when no usable preload response exists. Accept a response only when URL, identity, and cache policy match. Compare support, startup time, TTFB, cache hits, duplicate requests, and fallback errors before and after rollout.”
Step-by-step deep answer
Step 1: Enable it during activate
After checking registration.navigationPreload, call enable() inside activate’s waitUntil so configuration completes before the new worker is considered ready. setHeaderValue() can add context, but the value must not contain private or uncacheable state.
Step 2: Consume preloadResponse in fetch
Read event.preloadResponse only for navigation requests. It may resolve to a Response or undefined. Prefer a valid preload response; otherwise follow the cache or ordinary fetch policy. Do not start a second network request before deciding whether the preload promise has produced a usable result.
Step 3: Define cache and identity boundaries
SSR HTML containing identity, geography, or experiment data must respect existing cache keys and response headers. Do not unconditionally put it in Cache Storage. A static shell may be cache-first; a personalized page may be network-first, with Cookie, Authorization, and Vary behavior tested explicitly.
Step 4: Handle the header and server routing
The server can inspect Service-Worker-Navigation-Preload to recognize the parallel request and skip expensive work or use a dedicated cache. Proxies and CDNs need an explicit rule for forwarding, ignoring, or varying on that header so it cannot create cross-user cache sharing.
Step 5: Handle races, timeouts, and errors
If preload fails, times out, or returns an unsuitable status, fall back to cache or ordinary fetch according to policy. A Response body is normally single-use; use clone() when two consumers are required, and bound cancellation and timeout. Try an offline page after a network error, but do not hide a real server error.
Step 6: Degrade and update safely
Unsupported browsers continue with ordinary Service Worker fetch; browsers without Service Worker support use the network. During worker updates, preserve the old cache protocol and defer deletion until the new worker is usable, avoiding activation-time outages.
Step 7: Measure real performance and correctness
Compare cold and warm starts, slow network, offline mode, uncontrolled first visits, and worker updates. Record navigation-to-response, TTFB, worker startup, preload hits, cache hits, duplicate requests, and fallback errors. Verify identity isolation and browser behavior, not just average load time.
High-quality sample answer
I would feature-detect and enable navigation preload in activate, then await preloadResponse first in the navigation fetch handler. Only when it is absent or unsuitable would the handler use cache or ordinary fetch. Cache policy must separate static shells from personalized SSR HTML, and the server may use the preload header without changing cache isolation. Unsupported browsers retain ordinary fetch behavior. I would test cold starts, slow networks, offline mode, uncontrolled visits, updates, and duplicate-request counts while comparing TTFB and startup time.
Common mistakes
- Mistake: Treating navigation preload as static-resource precaching. → Why it fails: It targets navigation and runs alongside worker startup. → Fix: Consume
preloadResponsein the fetch handler. - Mistake: Starting an unconditional fetch when preload has no immediate result. → Why it fails: It can duplicate requests and side effects. → Fix: Define the promise, cache, and fetch order.
- Mistake: Writing personalized HTML to shared Cache Storage. → Why it fails: Cookie, Authorization, or Vary boundaries are ignored. → Fix: Use an identity-aware network-first policy.
- Mistake: Comparing only average load time. → Why it fails: Cold starts, uncontrolled visits, and duplicate requests disappear in the mean. → Fix: Track TTFB, startup, hits, and errors by cohort.
Follow-up questions and answers
Follow-up 1: Why not use link preload directly?
link rel=preload is page-declared and resource-oriented. Navigation preload is a browser request for navigation that runs during Service Worker startup; its lifecycle and consumption API are different.
Follow-up 2: Why can preloadResponse be undefined?
The browser may not support it, the request may not be a navigation, preload may be disabled, or the network request may fail before the worker event. Treat undefined as a normal branch.
Follow-up 3: How do you avoid reading a Response body twice?
A Response body is normally single-use. Call clone() when the page and cache both need it, and handle errors and cancellation in both consumers.
Follow-up 4: Why might first visit still see no speedup?
An uncontrolled page does not route navigation through the current worker, and registration, installation, and activation take time. Measure uncontrolled first visits separately instead of labeling them preload failures.