ScreenshotNeo

BlogHow-to

How to Intercept Network Requests with Puppeteer

Learn to continue, block, or mock Puppeteer requests, avoid stalled pages, handle multiple listeners, and troubleshoot common interception issues.

By the ScreenshotNeo team4 October 20267 min read

To intercept network requests with Puppeteer, enable interception with await page.setRequestInterception(true), then register a request handler before navigation. Resolve every intercepted request with request.continue(), request.abort(), or request.respond(). Requests stall until resolved, unless the browser cache completes them.

1. Set up request interception

This runnable example blocks requests whose URLs end in .png or .jpg and continues all other requests. Treat extension matching as a simple example, not a general way to identify images: URLs may omit extensions, use query strings, or return different content types.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();

  await page.setRequestInterception(true);
  page.on('request', request => {
    if (request.isInterceptResolutionHandled()) return;

    if (request.url().endsWith('.png') || request.url().endsWith('.jpg')) {
      request.abort();
    } else {
      request.continue();
    }
  });

  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  console.log(await page.title());
} finally {
  await browser.close();
}

Install Puppeteer in a Node.js project with npm install puppeteer. Save the example in an ES module file such as intercept.mjs, then run node intercept.mjs. The interception API is documented in the Page.setRequestInterception reference and the network logging guide.

2. Choose what to do with each request

Action Effect Use it when
continue() Sends the request onward, optionally with overrides. The resource should load, possibly with changed headers or method data.
abort() Stops the request; page code observes a failed request. The resource should be blocked.
respond() Supplies a response without sending the original request. You need a deterministic mock or fixture.

Every request that reaches the listener needs a resolution. If you only want to log URLs, interception is unnecessary; Puppeteer emits request and response events by default. Logging listeners can observe traffic without taking control of it.

Continue with modified headers

Pass overrides to continue(). To remove a header, set its value to undefined in the override object.

page.on('request', request => {
  if (request.isInterceptResolutionHandled()) return;

  const headers = {
    ...request.headers(),
    'x-debug-source': 'puppeteer',
    'x-remove-this-header': undefined,
  };
  request.continue({ headers });
});

Header names are case-insensitive at the HTTP layer. Keep the override limited to the values your test needs; changing headers can affect caching, authentication, and server behavior.

Fulfill with a mock response

respond() fulfills the request locally. The response can include a status, content type, headers, and body. For example, this handler returns a JSON fixture for one API endpoint and forwards everything else:

page.on('request', request => {
  if (request.isInterceptResolutionHandled()) return;

  if (request.url() === 'https://example.com/api/status') {
    request.respond({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ ready: true }),
    });
  } else {
    request.continue();
  }
});

A data: URL is a special case: Puppeteer’s documented respond() behavior is a no-op for it, so do not rely on interception to replace that response.

Abort selectively

Match requests using criteria that fit your application: an exact URL, hostname, path, resource type, or another property exposed by the request. For example, to block requests whose reported resource type is an image:

page.on('request', request => {
  if (request.isInterceptResolutionHandled()) return;

  if (request.resourceType() === 'image') {
    request.abort();
  } else {
    request.continue();
  }
});

Blocking a resource can change page behavior. A page may depend on an image, script, font, or stylesheet for layout or application logic, so use narrow rules and check the resulting page.

3. Handle multiple listeners and asynchronous work

A dependency or another event listener may resolve a request before your handler does. Check isInterceptResolutionHandled() before resolving it. If your handler awaits work, check again after the await, immediately before calling continue(), abort(), or respond().

page.on('request', async request => {
  if (request.isInterceptResolutionHandled()) return;

  const shouldBlock = await decideWhetherToBlock(request.url());

  // Another handler could have resolved this request during the await.
  if (request.isInterceptResolutionHandled()) return;

  if (shouldBlock) {
    request.abort();
  } else {
    request.continue();
  }
});

Keep the final state check and resolution together in the same synchronous section. Do not put another await between them.

Cooperative interception priorities

Puppeteer supports cooperative resolution when every handler resolving a request provides a numeric priority. Handlers are allowed to run, and the highest priority resolution wins. For equal priorities, abort takes precedence over respond, which takes precedence over continue. If any handler resolves without a priority, legacy immediate resolution applies; a library should not assume other listeners use cooperative mode.

page.on('request', request => {
  if (request.isInterceptResolutionHandled()) return;

  if (request.url().includes('/blocked/')) {
    request.abort('blockedbyclient', 10);
  } else {
    request.continue({}, 0);
  }
});

Use cooperative priorities only when multiple handlers intentionally coordinate. For a single handler, ordinary resolution is simpler. Check the current network interception guide for the version-specific API details.

4. Observe traffic without controlling it

Request and response events are enabled by default. Use them when the task is inspection or logging rather than blocking or modifying traffic.

page.on('request', request => {
  console.log('request', request.method(), request.url());
});

page.on('response', response => {
  console.log('response', response.status(), response.url());
});

Puppeteer distinguishes a transport-level failure from an HTTP error response. A completed 404 or 503 is still an HTTP response and is associated with requestfinished; inspect response.status() when you need to detect such status codes. Use requestfailed for requests that fail before completing normally.

5. Connection-level URL patterns

Puppeteer also documents URL pattern allowlist and blocklist settings in ConnectOptions. The documented feature currently supports Chrome only, and Puppeteer cautions that it is not a complete network sandbox. Treat it as a connection-level restriction, separate from per-page interception, and consult the current ConnectOptions reference before using it. Do not rely on it as the sole security boundary for untrusted pages.

6. Troubleshooting

Symptom Likely cause Fix
Navigation or page activity stalls after enabling interception. A request was intercepted but never resolved. Make every branch call continue(), abort(), or respond(). Add a default continue path.
“Request is already handled” or an interception resolution error. Another listener or dependency resolved the request first. Check isInterceptResolutionHandled() before acting, and repeat the check after asynchronous work.
A mocked response does not affect a data: URL. Puppeteer documents respond() as a no-op for data URLs. Handle that URL through page content or application setup instead of expecting an intercepted mock response.
A request with a query string escapes an extension rule. The URL does not literally end with the extension. Match the URL pathname or request resource type rather than relying on a raw suffix test.
A 404 appears in the response event rather than the failure event. HTTP error statuses are completed responses, not necessarily failed network requests. Read response.status() and handle the status code explicitly.
A blocked request breaks rendering or page logic. The page depends on the resource. Narrow the filter, allow required resources, and verify the page’s expected state after navigation.
URL pattern restrictions do not work in the current browser setup. The documented ConnectOptions feature is Chrome-only. Confirm the browser and Puppeteer version, or use a page request handler for the needed behavior.

7. Performance, reliability, and cost

Interception adds handler work to each intercepted request, and every unresolved request can delay the page. Keep synchronous rules small, avoid unnecessary asynchronous lookups, and use passive event listeners for observation alone. If a rule calls out to a service, consider caching its decision within your script and always protect the final resolution with the handled-state check.

Blocking images or other resources can reduce transferred work, but it can also make a page incomplete or change scripts’ behavior. Mocking makes tests more deterministic, but a fixture can drift from the live service contract. Use explicit timeouts and a deliberate navigation condition such as domcontentloaded when a page’s long-lived connections make network-idle waiting unsuitable.

Puppeteer request interception itself has no per-request fee stated in the cited API documentation. Your costs depend on where Chrome runs and any services your handler calls; no fixed cost or speedup should be assumed. Measure the actual workload if resource reduction or timing is important.

Or skip the browser setup

If your goal is to get a website screenshot rather than control each request, ScreenshotNeo is a website screenshot API and MCP server. One GET request captures a URL as PNG, JPEG, WebP, or PDF. See the 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
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo accepts cookie or consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card required.

FAQ

Can I intercept requests only after navigation starts?

Register the listener and enable interception before navigating if you need to handle the navigation’s requests. Otherwise early requests may already have begun.

Does interception block traffic from every page in the browser?

The example enables interception on one page. Configure each page where you need per-page request handling.

Can I change a request’s headers?

Yes. Pass header overrides to request.continue({ headers }) after enabling interception.

Should I use interception to collect request URLs?

No. Attach request and response listeners for observation; interception is for changing, stopping, or replacing traffic.