FetchEvent: respondWith() method
{{APIRef("Service Workers API")}} {{AvailableInWorkers("service")}}
The respondWith() method of
{{domxref("FetchEvent")}} prevents the browser’s default fetch handling, and
allows you to provide a promise for a {{domxref("Response")}} yourself.
In most cases you can provide any response that the receiver understands. For example,
if an {{HTMLElement('img')}} initiates the request, the response body needs to be
image data. For security reasons, there are a few global rules:
- You can only return
{{domxref("Response")}}objects of{{domxref("Response.type", "type")}}"opaque"if the{{domxref("fetchEvent.request")}}object’s{{domxref("request.mode", "mode")}}is"no-cors". This prevents the leaking of private data. - You can only return
{{domxref("Response")}}objects of{{domxref("Response.type", "type")}}"opaqueredirect"if the{{domxref("fetchEvent.request")}}object’s{{domxref("request.mode", "mode")}}is"manual". - You cannot return
{{domxref("Response")}}objects of{{domxref("Response.type", "type")}}"cors"if the{{domxref("fetchEvent.request")}}object’s{{domxref("request.mode", "mode")}}is"same-origin".
Specifying the final URL of a resource
From Firefox 59 onwards, when a service worker provides a {{domxref("Response")}} to
FetchEvent.respondWith(), the {{domxref("Response.url")}} value will be
propagated to the intercepted network request as the final resolved URL. If the
{{domxref("Response.url")}} value is the empty string, then the
{{domxref("Request.url","FetchEvent.request.url")}} is used as the final URL.
In the past the {{domxref("Request.url","FetchEvent.request.url")}} was used as the
final URL in all cases. The provided {{domxref("Response.url")}} was effectively
ignored.
This means, for example, if a service worker intercepts a stylesheet or worker script,
then the provided {{domxref("Response.url")}} will be used to resolve any relative
{{cssxref("@import")}} or
{{domxref("WorkerGlobalScope.importScripts()","importScripts()")}} subresource loads
(Firefox bug 1222008).
For most types of network request this change has no impact because you can’t observe the final URL. There are a few, though, where it does matter:
- If a
{{domxref("Window/fetch", "fetch()")}}is intercepted, then you can observe the final URL on the result’s{{domxref("Response.url")}}. - If a worker script is
intercepted, then the final URL is used to set
self.locationand used as the base URL for relative URLs in the worker script. - If a stylesheet is intercepted, then the final URL is used as the base URL for
resolving relative
{{cssxref("@import")}}loads.
Note that navigation requests for {{domxref("Window","Windows")}} and
{{domxref("HTMLIFrameElement","iframes")}} do NOT use the final URL. The way the HTML
specification handles redirects for navigations ends up using the request URL for the
resulting {{domxref("Window.location")}} . This means sites can still provide an
“alternate” view of a web page when offline without changing the user-visible URL.
Syntax
respondWith(response)
Parameters
response- : A
{{domxref("Response")}}or a{{jsxref("Promise")}}that resolves to aResponse. Otherwise, a network error is returned to Fetch.
- : A
Return value
None ({{jsxref("undefined")}} ).
Exceptions
NetworkError{{domxref("DOMException")}}- : Returned if a network error is triggered on certain combinations of
{{domxref("Request.mode","FetchEvent.request.mode")}}and{{domxref("Response.type")}}values, as hinted at in the “global rules” listed above.
- : Returned if a network error is triggered on certain combinations of
InvalidStateError{{domxref("DOMException")}}- : Returned if the event has not been dispatched or
respondWith()has already been invoked.
- : Returned if the event has not been dispatched or
Examples
This fetch event tries to return a response from the cache API, falling back to the network otherwise.
addEventListener("fetch", (event) => {
// Prevent the default, and handle the request ourselves.
event.respondWith(
(async () => {
// Try to get the response from a cache.
const cachedResponse = await caches.match(event.request);
// Return it if we found one.
if (cachedResponse) return cachedResponse;
// If we didn't find a match in the cache, use the network.
return fetch(event.request);
})(),
);
});
[!NOTE]
{{domxref("CacheStorage.match()", "caches.match()")}}is a convenience method. Equivalent functionality is to call{{domxref("cache.match()")}}on each cache (in the order returned by{{domxref("CacheStorage.keys()", "caches.keys()")}}) until a{{domxref("Response")}}is returned.
Specifications
{{Specifications}}
Browser compatibility
{{Compat}}