Skip to main content
SeleniumDecoded

Block Analytics and Third-Party Requests

Stop trackers, chat widgets and ad scripts from loading during tests to speed up pages and remove external flakiness.

Medium Verified against Selenium 4.48.0

Problem

Every page load waits for Google Tag Manager, a chat widget and two ad networks. They add seconds, occasionally hang, and pollute analytics with test traffic.

Solution

Intercept requests to those hosts with BiDi and fail them. Put the block list in the driver factory so every test benefits.

Fail requests to a host block list
Selenium 4 Medium
import org.openqa.selenium.bidi.module.Network;
import org.openqa.selenium.bidi.network.*;
static final List<String> BLOCKED = List.of(
"*.googletagmanager.com", "*.google-analytics.com", "*.doubleclick.net",
"*.hotjar.com", "*.intercom.io", "widget.intercom.io");
public static void blockThirdParties(WebDriver driver) {
Network network = new Network(driver);
List<UrlPattern> patterns = BLOCKED.stream().map(h -> new UrlPattern().hostname(h)).toList();
network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT).urlPatterns(patterns));
network.onBeforeRequestSent(e -> {
if (e.isBlocked()) network.failRequest(e.getRequest().getRequestId());
});
}
BLOCKED = ["**googletagmanager.com/**", "**google-analytics.com/**", "**doubleclick.net/**",
"**hotjar.com/**", "**intercom.io/**"]
def block_third_parties(driver):
driver.network.add_request_handler(BLOCKED, lambda request: request.fail())
const { Network } = require('selenium-webdriver/bidi/network');
const { AddInterceptParameters } = require('selenium-webdriver/bidi/addInterceptParameters');
const { InterceptPhase } = require('selenium-webdriver/bidi/interceptPhase');
const BLOCKED = ['googletagmanager.com', 'google-analytics.com', 'doubleclick.net', 'hotjar.com', 'intercom.io'];
async function blockThirdParties(driver) {
const network = await Network(driver);
await network.addIntercept(new AddInterceptParameters(InterceptPhase.BEFORE_REQUEST_SENT));
await network.beforeRequestSent(async (e) => {
if (!e.isBlocked) return;
const host = new URL(e.request.url).hostname;
if (BLOCKED.some((b) => host.endsWith(b))) await network.failRequest(e.request.request);
else await network.continueRequest(new ContinueRequestParameters(e.request.request));
});
}
static readonly string[] Blocked = { "googletagmanager.com", "google-analytics.com", "doubleclick.net", "hotjar.com", "intercom.io" };
public static async Task BlockThirdPartiesAsync(IWebDriver driver)
{
var bidi = await driver.AsBiDiAsync();
await bidi.Network.InterceptRequestAsync(async e =>
{
var host = new Uri(e.Request.Url).Host;
if (Blocked.Any(b => host.EndsWith(b))) await e.Request.FailAsync();
else await e.Request.ContinueAsync();
});
}

Why It Works

Failing the request at beforeRequestSent means the browser never opens a connection; the page sees a network error for that resource and continues. Well-built apps tolerate a missing analytics script; if yours does not, that is a finding.

Gotchas

  • When intercepting all requests (JavaScript and C# examples), you must continue the ones you do not block, or the page hangs. The Java and Python examples only intercept matching hosts, so no continuation is needed.
  • Some consent-management scripts block the page until they load; add them to the list and dismiss the banner via cookie instead.
  • Without BiDi, Chrome’s --host-resolver-rules="MAP *.doubleclick.net 127.0.0.1" argument achieves a cruder block.
  • Keep one test unblocked to catch integration breakage with the real third parties.

Learn the theory