How to Check Whether a Puppeteer Response Came from a Service Worker
Use Puppeteer’s `HTTPResponse.fromServiceWorker()` to check whether a response was served by a service worker, and distinguish it from browser cache and request interception.
Call HTTPResponse.fromServiceWorker() on the response. It returns a boolean: true means the response was served by a service worker. In a page response listener, Puppeteer gives you the HTTPResponse to inspect.
page.on('response', response => {
if (response.fromServiceWorker()) {
console.log('Served by a service worker:', response.url());
}
});
See Puppeteer’s API reference for HTTPResponse.fromServiceWorker(). Its API is documented separately from the response’s browser-cache check, fromCache().
Complete runnable example
The following Node.js example launches Chromium, navigates to a page, and reports responses served by a service worker. Replace the example URL with your page. The listener is attached before navigation so it can observe responses during the load.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
page.on('response', response => {
if (response.fromServiceWorker()) {
console.log('Service worker response:', response.url());
}
});
await page.goto('https://example.com', {
waitUntil: 'networkidle2',
timeout: 30000,
});
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
Install Puppeteer in a project with npm install puppeteer, save the code as check-sw.js, then run node check-sw.js. This example reports matching responses rather than assuming every request will be controlled by a service worker. The target page must register and use a service worker for the check to return true for relevant responses.
Check a response after a specific navigation
To collect results for one navigation, keep a list in the listener and inspect it after page.goto(). A response event can occur for documents and subresources, so filter by URL or resource type if you only care about one request.
const responses = [];
page.on('response', response => {
responses.push({
url: response.url(),
fromServiceWorker: response.fromServiceWorker(),
fromCache: response.fromCache(),
status: response.status(),
});
});
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
console.table(responses.filter(item => item.fromServiceWorker));
For one known request, match the URL or use the corresponding request object to inspect its response:
const targetUrl = 'https://example.com/data.json';
const targetResponsePromise = page.waitForResponse(response =>
response.url() === targetUrl
);
await page.goto('https://example.com');
const response = await targetResponsePromise;
console.log({
url: response.url(),
fromServiceWorker: response.fromServiceWorker(),
fromCache: response.fromCache(),
status: response.status(),
});
When waiting for a response associated with a navigation, register the waiter before triggering the navigation or action; otherwise the response may arrive before the wait begins. If a URL can be requested more than once, make the predicate more specific, for example by checking the request method or resource type.
Interpret the result correctly
| Check | What it tells you | Use it for |
|---|---|---|
response.fromServiceWorker() |
Whether the response was served by a service worker. | Response provenance. |
response.fromCache() |
Whether the response came from the browser’s disk or memory cache. | Browser cache diagnostics. |
request.isInterceptResolutionHandled() or request.interceptResolutionState() |
The status of Puppeteer’s request-interception resolution. | Debugging code that intercepts and continues, fulfills, or aborts requests. |
These checks answer different questions. A cache result does not establish service-worker provenance, and interception state is not a substitute for fromServiceWorker(). Puppeteer documents interception as a process where intercepted requests stall until they are resolved. See the request interception guide.
Also distinguish an HTTP error response from a failed request. A 404 or 503 still produces a response event; its status code alone does not mean the request failed at the network level. Inspect response.status() for the HTTP result and the request-failure event when diagnosing an actual loading failure. See Puppeteer’s page event documentation.
Options and practical details
Observe all responses or filter them
The page.on('response') listener sees responses for the page, including subresources. Filter the URL, request method, or resource type to narrow output. For example:
page.on('response', response => {
const request = response.request();
if (request.resourceType() === 'xhr' || request.resourceType() === 'fetch') {
console.log(response.url(), response.fromServiceWorker());
}
});
Use the installed Puppeteer version
The method is part of Puppeteer’s HTTPResponse API. The reviewed official API reference displayed version 25.4.0; check the versioned documentation matching your installed package if behavior or signatures are in doubt. Avoid assuming documentation for a different release precisely matches your project.
Service worker control matters
A page having a service worker registration does not mean every response came from it. The worker must be active and control the relevant page/request path. First load, registration timing, scope, and whether a request is handled by the worker can affect what you observe. Use the response method as the direct check rather than inferring provenance from registration or from a URL.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
fromServiceWorker() is false |
The response was fetched from the network or browser cache, or the worker did not control or handle it. | Confirm the page registers an active worker with a scope covering the page, and inspect the exact response you care about. |
| No response log appears | The listener was attached after the response, navigation did not reach the request, or the request failed before producing a response. | Attach the listener before navigation; verify the URL and inspect request-failure events and navigation errors. |
| Results include many unrelated URLs | The page response event includes subresource responses. | Filter by URL, method, or resource type. |
| Confusion between cache and worker | fromCache() and fromServiceWorker() report different sources. |
Log both booleans independently; do not infer one from the other. |
| Interception code reports a handled request | That value describes request resolution, not the response source. | Use response.fromServiceWorker() after a response exists. |
| Navigation times out or returns an HTTP error | A timeout is a navigation/load problem; an HTTP status such as 404 is still a response outcome. | Handle navigation exceptions separately and inspect response status codes rather than treating all non-2xx results as request failures. |
Performance, reliability, and cost
Checking fromServiceWorker() on response events is a small diagnostic operation. The larger costs are launching Chromium, loading the page and its resources, and waiting for the chosen navigation condition. For a large page, filter events and store only the fields needed; collecting every response object for a long crawl can use unnecessary memory.
For reliable diagnostics, attach listeners before navigation, use a response predicate that identifies the intended request, and keep navigation timeouts separate from HTTP status handling. Service worker lifecycle and page control can vary with the test setup, so report the observed boolean for each response rather than assuming the worker handled the page because it is registered.
Or skip the browser setup
If your goal is to capture a page as an image or PDF rather than inspect Puppeteer response provenance, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call API returns a screenshot; it does not expose Puppeteer’s service-worker provenance check. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, and cache hits are never billed. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.
FAQ
Does fromServiceWorker() return a promise?
No. It returns a boolean on the HTTPResponse.
Can a 404 response come from a service worker?
Yes. HTTP status and response provenance are separate properties. Check the status and fromServiceWorker() independently.
Does enabling request interception tell me whether the worker served a response?
No. Interception methods describe resolution of Puppeteer’s intercepted request. Use the response’s fromServiceWorker() method for provenance.


