Skip Images and Fonts in Puppeteer to Speed Up Headless Loads
Reduce Puppeteer bandwidth and load time by aborting image, stylesheet, and font requests with request interception.
15 May 2026, 11:05 UTC

Your headless browser is downloading assets you never read
If your Puppeteer script waits for a page to settle before extracting text, Chromium is probably still pulling hero images, web fonts, and stylesheets your automation never displays. Those requests consume bandwidth and add latency, yet the data you need—article body text, form values, or JavaScript state—often arrives before the last image finishes.
The useful takeaway is narrow: enable Puppeteer request interception, abort image, stylesheet, font, and media requests, and let documents, scripts, and API calls continue. You keep DOM parsing and JavaScript execution while skipping bytes you do not need.
Abort optional requests with setRequestInterception
page.setRequestInterception(true) tells Chromium to pause every page-initiated network request and hand it to a Node.js handler. In that handler you call request.abort(), request.continue(), or request.respond(). Chromium exposes a resourceType() for each request, including document, stylesheet, image, font, media, script, xhr, and fetch.
Interception must be enabled before navigation or before the targeted requests fire, and it stays active for that page until you turn it off. It works the same for launched browsers and browserWSEndpoint connections.
Worked example: skip images, stylesheets, and fonts
Save this as skip-assets.js and run it with node skip-assets.js. No special system permissions are required beyond normal Node.js execution.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Turn on interception before any navigation.
await page.setRequestInterception(true);
// Log every request so you can verify what was blocked.
page.on('request', (request) => {
console.log(`-> ${request.resourceType()} ${request.url()}`);
});
page.on('request', (request) => {
const blockedTypes = ['image', 'stylesheet', 'font', 'media'];
if (blockedTypes.includes(request.resourceType())) {
request.abort();
} else {
request.continue();
}
});
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
// Extract or inspect what you need here.
const title = await page.title();
console.log('Page title:', title);
await browser.close();
})();
The first page.on('request') listener is diagnostic only; it logs requests without changing them. The second listener performs the actual blocking. If you remove the diagnostic listener in production, you reduce per-request callback overhead.
Expected checks: image, stylesheet, font, and media URLs should appear in the log with no corresponding network activity in Chromium, while document, script, and XHR requests complete normally. A practical risk is that aborting stylesheets can leave pages unstyled and trigger layout recalculations after the DOM is ready.
Trade-offs: unstyled pages, broken APIs, and detection
- Stylesheets: Blocking CSS can cause a flash of unstyled content and force the browser to recalculate layout once styles eventually arrive or are omitted entirely.
- XHR and fetch: Aborting these can break single-page applications that load core content through APIs. Only block them if you are certain the page does not depend on API responses for the data you need.
- Overhead: Every intercepted request runs a Node.js callback. For high-throughput crawling, network-level blocking may be more efficient than Puppeteer interception.
- Detection: Sites may notice absent resources or unusual request patterns and respond with challenges, CAPTCHAs, or degraded experiences.
- Scope: Interception covers page-initiated requests only, not browser extensions or browser UI.
Verify before you ship it
Run the same navigation twice: once with interception enabled and once with interception disabled. Use a fresh browser context or disable HTTP caching so cached assets do not hide the difference. Log request.resourceType() and request.url() on both runs, then compare request count and load time. Open DevTools in headful mode and confirm image and CSS requests are canceled while HTML and JavaScript requests complete. Repeat several times; a single fast run may be caching, not interception.
Bottom line
Start by blocking images and fonts, which are usually safe to omit. Treat stylesheets as optional and test for layout shifts. Avoid blocking XHR or fetch unless you have confirmed the page renders its content without API calls. If the speedup is not repeatable across multiple runs, investigate caching or site detection before relying on interception in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.