ScreenshotNeo

BlogHow-to

How to Abort an HTTP Request in Puppeteer

Use Puppeteer request interception to abort selected HTTP requests while letting the rest of the page load. Includes matching patterns, error handling, and a ScreenshotNeo option.

By the ScreenshotNeo team4 October 20267 min read

To abort selected HTTP requests in Puppeteer, enable request interception before navigation, then handle every intercepted request by aborting, continuing, or responding to it. For example, this blocks PNG and JPG requests while allowing other traffic through:

await page.setRequestInterception(true);

page.on('request', request => {
  if (request.url().endsWith('.png') || request.url().endsWith('.jpg')) {
    request.abort();
  } else {
    request.continue();
  }
});

await page.goto('https://example.com');

The example uses Puppeteer’s Page and HTTPRequest APIs. See the Page.setRequestInterception API and HTTPRequest API. API details can vary by Puppeteer version; the current documentation in the research dossier identifies version 25.12.0.

1. Set up Puppeteer

Install Puppeteer in a Node.js project:

npm install puppeteer

This complete script launches a browser, enables interception before navigation, blocks image resources, loads a page, and closes the browser even if navigation fails:

const puppeteer = require('puppeteer');

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

    await page.setRequestInterception(true);
    page.on('request', request => {
      if (request.resourceType() === 'image') {
        request.abort().catch(error => {
          console.error('Could not abort request:', request.url(), error);
        });
      } else {
        request.continue().catch(error => {
          console.error('Could not continue request:', request.url(), error);
        });
      }
    });

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

Save it as abort-requests.js and run node abort-requests.js. The request listener resolves each request asynchronously, so the example catches resolution errors rather than leaving rejected promises unobserved. In applications with other request handlers, add the guard described below.

2. Choose what to abort

The request object exposes properties you can use to build a narrow predicate. Prefer a condition that matches only traffic you intend to stop.

Match on Example When it helps
Resource type request.resourceType() === 'image' Block a class of resources such as images, fonts, stylesheets, or media.
URL request.url().includes('/analytics/') Target a known path or endpoint.
Hostname new URL(request.url()).hostname === 'tracker.example' Block requests to a specific host.
Method request.method() === 'POST' Target a request method, ideally combined with a URL condition.
Navigation request request.isNavigationRequest() Distinguish document navigation from subresource traffic.

For example, block one analytics endpoint and continue everything else:

await page.setRequestInterception(true);

page.on('request', request => {
  const shouldBlock = request.url().includes('tracker.example/collect');
  if (shouldBlock) {
    request.abort();
  } else {
    request.continue();
  }
});

To combine checks, use a predicate that explicitly expresses the target. This blocks only image requests served by a particular host:

await page.setRequestInterception(true);

page.on('request', request => {
  let shouldBlock = false;
  try {
    const url = new URL(request.url());
    shouldBlock = url.hostname === 'static.example' &&
      request.resourceType() === 'image';
  } catch {
    // If a request URL cannot be parsed, allow it through.
  }

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

Allowing unrecognized URLs through is a conservative default when the intent is to block only a known category. Blocking too broadly can prevent the document, scripts, styles, or API calls needed by the page.

3. Resolve every intercepted request

With interception enabled, requests pause until they are continued, aborted, responded to, or completed using the browser cache. Every handler path therefore needs to resolve the request. The three explicit actions are:

  • request.abort() stops the request.
  • request.continue() lets it proceed, optionally with overrides such as modified headers.
  • request.respond() fulfills it with a response you provide, useful for controlled mocks.

Here is an example that mocks one endpoint and continues all other requests:

await page.setRequestInterception(true);

page.on('request', request => {
  if (request.url().endsWith('/feature-flags')) {
    request.respond({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({ newLayout: false }),
    });
  } else {
    request.continue();
  }
});

Do not leave a matching branch without an action. An unresolved intercepted request can stall page loading or code waiting for a response.

4. Avoid conflicts with other handlers

If another listener or package may resolve the same request, check whether it has already been handled. Keep the check immediately beside the action and do not await between them; another handler could resolve the request while an asynchronous handler is waiting.

await page.setRequestInterception(true);

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

  const shouldBlock = request.url().includes('/analytics/');
  if (shouldBlock) {
    request.abort();
  } else {
    request.continue();
  }
});

If a handler must do asynchronous work before deciding, re-check immediately before resolving:

page.on('request', async request => {
  const shouldBlock = await determineWhetherToBlock(request.url());

  // Another listener may have handled the request during the await.
  if (request.isInterceptResolutionHandled()) return;

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

For several components that need to make interception decisions, Puppeteer documents cooperative interception priorities. Each resolution must supply a numeric priority to participate in cooperative mode. The highest priority wins; ties resolve in the order abort, respond, continue. A handler that omits a priority uses legacy immediate resolution, so the handled check still matters when combining packages. Consult the Puppeteer network interception guide for the version you use.

5. Observe aborts and distinguish HTTP errors

An aborted request emits requestfailed. A server response such as 404 or 503 is still an HTTP response and completes with requestfinished. Log both events to understand whether Puppeteer blocked a request or the server returned an error:

page.on('requestfailed', request => {
  console.log('Request failed or was aborted:', request.url(), request.failure());
});

page.on('requestfinished', request => {
  console.log('Request completed:', request.url());
});

page.on('response', response => {
  if (response.status() >= 400) {
    console.log('HTTP error response:', response.status(), response.url());
  }
});

Use the event appropriate to the question: requestfailed indicates a request-level failure, while the response status identifies an HTTP error returned by a server.

6. Run the browser yourself or use an API

Local interception is useful when your application needs custom routing, request mocks, or browser automation that depends on the blocked traffic. It also means your service owns browser installation, concurrency, navigation timeouts, and output handling.

For a screenshot workflow that does not need custom Puppeteer request logic, ScreenshotNeo is a website screenshot API and MCP server. Its clean-shot flow accepts cookie and consent banners before capture and removes 60+ known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Its response reports the page verdict and billing status, and bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. See ScreenshotNeo and its API documentation.

7. Performance, reliability, and cost

Performance

Aborting unnecessary resources can reduce traffic and let the page avoid waiting on those resources, but the result depends on what the page needs and which navigation condition you choose. For example, domcontentloaded does not wait for every resource; blocking requests is not a substitute for selecting an appropriate waitUntil condition. Measure the behavior in your own workload rather than assuming every blocked request speeds up the complete job.

Reliability

  • Enable interception before navigation or before the requests you intend to control.
  • Make every handler path resolve the request.
  • Keep match conditions narrow and default to continuing unrelated traffic.
  • Guard against duplicate resolution when multiple listeners or packages are involved.
  • Check the Puppeteer documentation for your installed version and investigate specialized request sources separately; the documented behavior does not establish universal behavior for every service worker or browser version.

Cost

Self-hosted Puppeteer has no per-request ScreenshotNeo charge, but you operate the browser environment and pay the associated compute, network, and maintenance costs. If you use ScreenshotNeo instead, the listed plans are Free: 1,000 shots per month with no card; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; and Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. Use the plan that matches your capture volume.

8. Troubleshooting

Symptom Likely cause Fix
request.abort() throws immediately Request interception is not enabled. Call and await page.setRequestInterception(true) before installing the flow that aborts requests.
The page hangs or navigation times out A request handler path did not resolve an intercepted request, or a necessary resource was blocked. Ensure all branches abort, continue, or respond. Narrow the match and continue unrelated requests.
A request action reports that it was already handled Another event listener or package resolved it first. Check request.isInterceptResolutionHandled() immediately before resolution. If asynchronous work occurred, check again after it.
Images or styles are missing unexpectedly The predicate matches more requests than intended, perhaps by suffix or broad substring. Inspect request URLs and resource types, then combine hostname, path, and type checks as needed.
A request appears failed but the server may be at fault requestfailed and HTTP error responses represent different outcomes. Log request.failure() and listen for responses with status 400 or higher.
A request handler’s decision seems inconsistent across packages One handler may use legacy immediate resolution while others expect cooperative priorities. Use the documented cooperative priorities consistently and retain handled checks for legacy handlers.

9. Or skip the browser setup

For a screenshot without managing Puppeteer, make one GET request. Replace YOUR_API_KEY with an API key and change the target URL. See the ScreenshotNeo API documentation for parameters and response details.

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}`);

ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up for free and get 1,000 screenshots a month with no card.

10. FAQ

Can I abort only one URL?

Yes. Compare request.url() to the exact URL or a carefully chosen path, then continue every nonmatching request.

Does aborting a request mean the server returned an error?

No. An abort is a request failure event. A 404 or 503 is an HTTP response from the server.

Can I replace a blocked request with test data?

Yes. Use request.respond() with a status, content type, and body to fulfill a matching request with a mock response.