Reducing E2E Test Flakiness by Blocking Third-Party Requests in Puppeteer
Learn how to use Puppeteer's request interception to block third-party analytics and ads, reducing E2E test flakiness and improving execution speed.
05 Aug 2026, 11:47 UTC

The Noise Problem in End-to-End Testing
When running end-to-end (E2E) tests, your application rarely exists in a vacuum. It loads analytics scripts, tracking pixels, ad networks, and external fonts. While these are essential for production, they are liabilities in a CI/CD pipeline. A slow response from a third-party analytics provider can cause a timeout, and an unexpected popup from a marketing script can obstruct a button your test needs to click, leading to "flaky" tests that fail randomly.
The most effective way to stabilize these tests is to prevent the browser from ever requesting these external resources. Puppeteer provides a built-in mechanism called Request Interception that allows you to inspect and abort outgoing network requests before they leave the browser.
Implementing Request Interception
Request interception is enabled on a per-page basis. Once activated, Puppeteer pauses every network request and triggers an event that you must handle. If you enable interception but fail to call request.continue(), request.abort(), or request.respond(), the page will hang indefinitely because the browser is waiting for instructions on how to proceed.
Crucially, page.setRequestInterception(true) must be called before you navigate to the URL. If you call it after page.goto(), the initial HTML and the first wave of linked assets will have already been requested, defeating the purpose of blocking them.
Worked Example: Blocking Tracking and Analytics
In this example, we target common third-party patterns (like Google Analytics or Facebook Pixel) to ensure they don't interfere with the test execution. This code should be run in a Node.js environment with puppeteer installed.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 1. Enable interception before navigation
await page.setRequestInterception(true);
page.on('request', (request) => {
const url = request.url();
// Define a list of domains or keywords to block
const blockedResources = ['google-analytics.com', 'doubleclick.net', 'facebook.net'];
const shouldBlock = blockedResources.some(resource => url.includes(resource));
if (shouldBlock) {
// Abort the request to prevent it from hitting the network
request.abort();
} else {
// Allow all other requests to proceed normally
request.continue();
}
});
try {
await page.goto('https://your-app-under-test.com', { waitUntil: 'networkidle0' });
console.log('Page loaded without third-party trackers.');
} catch (err) {
console.error('Navigation failed:', err);
} finally {
await browser.close();
}
})();
Verification and Risks
To verify this is working, run the script in headful mode (headless: false) and open the Chrome DevTools Network tab. Requests to the blocked domains should appear with a status of (blocked:puppeteer).
Risk: If your application logic depends on a third-party script to initialize a UI component (e.g., a Stripe payment element or a Google Map), blocking that domain will cause your application to crash or the component to fail to render. Always audit your dependencies before adding a domain to the blocklist.
Engineering Trade-offs
While blocking requests improves speed and stability, it introduces a gap in your testing strategy. You are no longer testing the integration of those third-party services. If a third-party API changes its response format or goes offline, your E2E tests will still pass because the browser never attempted the connection.
Additionally, request interception introduces a small amount of overhead. Because every single request must be passed from the browser process to the Node.js process for evaluation, pages with hundreds of small assets may see a slight increase in CPU usage on the test runner.
| Approach | Pros | Cons |
|---|---|---|
| Allow All | Tests real-world integration | High flakiness, slower execution |
| Interception | Fast, deterministic, stable | Masks external API failures |
| Stub Server | Full control over responses | High infrastructure setup cost |
Actionable Summary
To stabilize your Puppeteer suite, implement request interception to prune non-essential network traffic. Start by blocking known analytics and ad domains. To mitigate the risk of masking integration failures, complement these blocked E2E tests with a separate set of "smoke tests" that run against the real external dependencies once per deployment, rather than on every commit.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.