ScreenshotNeo

BlogHow-to

How to Check Whether a Puppeteer Response Came from Cache

Use Puppeteer’s `HTTPResponse.fromCache()` to check browser cache delivery, and `fromServiceWorker()` to identify service-worker responses separately.

By the ScreenshotNeo team4 October 20266 min read

To check whether a Puppeteer response came from the browser’s disk or memory cache, call response.fromCache() on the HTTPResponse. Check response.fromServiceWorker() separately: service-worker delivery is a different response source. These flags are more reliable for this question than inferring from status codes, headers, or response time. See the Puppeteer HTTPResponse API.

Check a response as it arrives

Listen for the page’s response event. Its callback receives an HTTPResponse, so you can inspect the URL and both delivery flags directly:

page.on('response', response => {
  console.log({
    url: response.url(),
    fromBrowserCache: response.fromCache(),
    fromServiceWorker: response.fromServiceWorker(),
  });
});

The response object also provides request() if you need the corresponding HTTPRequest. Keep the two booleans distinct in logs: a service worker may handle a response without it being reported as an ordinary disk or memory cache hit.

Run a complete Puppeteer example

Install Puppeteer in a new project with npm install puppeteer, save this as check-cache.js, and run node check-cache.js. The script enables the response listener before navigation so it can record responses from the page load:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    const responses = [];

    page.on('response', response => {
      responses.push({
        url: response.url(),
        status: response.status(),
        fromBrowserCache: response.fromCache(),
        fromServiceWorker: response.fromServiceWorker(),
      });
    });

    await page.goto('https://example.com', { waitUntil: 'networkidle2' });
    console.table(responses);
  } finally {
    await browser.close();
  }
})().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

For a reusable helper, filter the captured rows after navigation:

const cacheResponses = responses.filter(item => item.fromBrowserCache);
const serviceWorkerResponses = responses.filter(item => item.fromServiceWorker);

console.log('Browser cache:', cacheResponses);
console.log('Service worker:', serviceWorkerResponses);

A fresh browser context often has no prior cache entries, so the first navigation may report no cache hits. To investigate repeat visits, navigate again in the same page or context and compare the recorded flags. Results depend on Chrome’s cache behavior and the page’s resources.

Use the cache event when you need a request-level signal

Puppeteer also emits requestservedfromcache when a request ended up loading from cache. Its payload is an HTTPRequest, but Puppeteer documents that the request can be undefined for certain requests. Guard against that case:

page.on('requestservedfromcache', request => {
  if (!request) {
    console.log('Cache event fired without a request object');
    return;
  }

  console.log('Request loaded from cache:', request.url());
});

Choose this event when you want to record cache-served requests as they occur. Choose response.fromCache() when you are inspecting a particular response and want response-level flags. The event’s documented missing-payload caveat is described in the Puppeteer PageEvent reference.

Distinguish disk, service-worker, and prefetch delivery with CDP

HTTPResponse.fromCache() is the direct, convenient check for disk or memory cache. If you need Chrome’s more granular source fields, listen to the Chrome DevTools Protocol’s Network.responseReceived event. Its response schema exposes optional fromDiskCache, fromServiceWorker, and fromPrefetchCache booleans. These categories are separate in the CDP Network protocol reference.

const client = await page.createCDPSession();
await client.send('Network.enable');

client.on('Network.responseReceived', event => {
  const response = event.response;
  console.log({
    url: response.url,
    fromDiskCache: response.fromDiskCache ?? false,
    fromServiceWorker: response.fromServiceWorker ?? false,
    fromPrefetchCache: response.fromPrefetchCache ?? false,
  });
});

await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await client.detach();

Attach the listener and enable the Network domain before navigation so the initial page responses are included. The CDP fields are optional, so treat a missing value as unknown or display it as false only when that is appropriate for your reporting. Puppeteer’s two methods are simpler when the disk-or-memory answer and service-worker distinction are enough.

Cache behavior, configuration, and interpretation

  • Use a persistent page or context for repeat-visit checks. Closing the browser or creating a fresh context can remove the conditions needed to observe a prior cache entry.
  • Do not infer a cache hit from speed. A quick response is not itself evidence that Chrome used its cache; inspect the documented flags.
  • Do not infer it from HTTP status or headers. A successful status, a cache-related header, or a response’s presence does not replace the browser’s source flags.
  • Keep service-worker results separate. Log fromServiceWorker() or the corresponding CDP field rather than grouping it automatically with browser cache.
  • Use CDP only when you need finer classification. The protocol adds disk and prefetch source fields, while fromCache() is the simpler disk-or-memory check.
  • Account for page scope. A page can load documents, scripts, images, and other resources. Filter the event records by URL or request type if your question concerns one resource.

These listeners add lightweight event handling and logging, but the page’s own network activity and chosen navigation wait condition usually dominate runtime. For reliable diagnostics, record the URL and source flags together, keep the browser context consistent between runs, and avoid treating an absent optional CDP field as definitive evidence about a source.

Troubleshooting

Symptom Likely cause What to do
fromCache() is always false The resource was not served from browser disk or memory cache in those navigations; a new context or first visit may have no reusable entry. Repeat the navigation in the same page or context and inspect the response records. Do not force a cache result based on response speed.
A response has fromServiceWorker() === true A service worker supplied the response. Report it in a separate service-worker field. If you need more source details, inspect the CDP response fields.
No response appears in the log The listener may have been attached after navigation, or that operation did not produce a response event. Register listeners before goto(). For requests that fail before receiving a response, also observe Puppeteer’s request failure event.
The cache event callback receives no request Puppeteer documents that the requestservedfromcache payload can be undefined for certain requests. Keep the null guard and use response-level flags when you need to associate a concrete response with a URL.
CDP cache fields are missing The fields are optional in the protocol schema and may not be supplied for every response. Represent missing values as unknown in detailed diagnostics; use Puppeteer’s fromCache() when its broader classification answers the question.
The script exits before output or reports a navigation error The target navigation failed or timed out before the script reached its output code. Catch the error, log it, and inspect the URL and navigation conditions. Keep the browser cleanup in a finally block.

Or skip the browser setup

If you need a screenshot rather than a Puppeteer cache diagnostic, ScreenshotNeo is a website screenshot API and MCP server for developers. It returns an image or PDF from one GET request. Its response identifies whether a result was a cache hit, and cache hits are not billed. See the ScreenshotNeo API docs.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed. Response headers say what happened and whether the result was billed.
  • An 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 plan and get 1,000 screenshots a month with no card.

FAQ

Does Puppeteer have a cache-hit event?

Yes. Listen for requestservedfromcache. Its request argument may be undefined for certain requests, so guard it.

Does fromCache() include service-worker responses?

Use fromServiceWorker() as the separate service-worker check. CDP also exposes service-worker delivery as a distinct field.

Can I tell memory cache from disk cache with fromCache()?

No. It reports the combined browser disk-or-memory cache result. CDP’s response schema includes a disk-cache flag for more granular diagnostics.

Does a fast response prove it came from cache?

No. Use the cache-source methods or protocol fields rather than response timing as a substitute.