Programmatic Request Interception with Chrome DevTools Protocol
Use Chrome DevTools Protocol to intercept and rewrite HTTP requests programmatically, inject auth tokens, block trackers, and simulate failures without browser extensions.
18 Aug 2025, 18:46 UTC

The Problem: Programmatic Network Control Without Extensions
\nEnd‑to‑end test suites often need to inject auth tokens into every API call, performance runs must block third‑party trackers, and scrapers may want to simulate API failures. Browser extensions are brittle, require manual installation, and don’t work in headless CI. The Chrome DevTools Protocol (CDP) Network domain, available since Chrome 63, gives you a stable automation surface to intercept, modify, and monitor HTTP traffic directly.
\nHow Interception Works at the Network Stack
\nWhen setRequestInterception is enabled with URL patterns, Chrome pauses matching requests at the network layer and emits a requestIntercepted event that includes an interceptionId. Your automation code must call continueInterceptedRequest (optionally with modified headers, body, or URL) or the request times out after roughly 30 seconds. The protocol runs over a WebSocket connection (ws://127.0.0.1:9222/devtools/page/) or through higher‑level libraries such as Puppeteer, Playwright, or Selenium 4’s CDP API. No extra flags are required beyond --remote-debugging-port=9222; headless mode is fully supported.
Worked Example: Injecting an Auth Token for E2E Tests
\nThe following minimal Puppeteer script connects to a running Chrome instance and adds an Authorization header to every request matching api.example.com:
const puppeteer = require('puppeteer');
async function injectAuthToken() {
// Connect to Chrome started with --remote-debugging-port=9222
const browser = await puppeteer.connect({ browserURL: 'http://127.0.0.1:9222' });
const page = (await browser.pages())[0];
// Enable interception at the page level
await page.setRequestInterception(true);
page.on('request', (request) => {
const url = request.url();
if (url.includes('api.example.com')) {
const headers = { ...request.headers(), 'Authorization': 'Bearer test-token-123' };
request.continue({ headers });
} else {
request.continue();
}
});
// Navigate and verify the header appears in server logs
await page.goto('https://app.example.com/dashboard');
}
injectAuthToken().catch(console.error);
\nWhere to run: Any Node.js environment with Puppeteer installed. Permissions: Chrome must be launched with --remote-debugging-port=9222 (bind to localhost only in shared environments). Expected check: Confirm the Authorization header appears in your API server access logs. Risk: If request.continue() is never called, the request hangs and eventually fails with net::ERR_FAILED.
Trade‑offs and Limitations You’ll Hit
\n- \n
- Latency overhead: Enabling interception adds ~1‑5 ms per request. For pure performance measurements, disable interception or keep patterns narrow. \n
- Deadlock risk: Failing to call
continueInterceptedRequestpromptly stalls the page. Always implement timeout guards in automation code. \n - Body modification constraints: Replacing POST/PUT bodies requires providing the full replacement; partial edits aren’t supported. Form‑data and multipart bodies must be reconstructed manually. \n
- Large response bodies:
getResponseBodyreturns base64‑encoded content. Bodies over ~10 MB may be truncated unless you usetakeResponseBodyForInterceptionAsStream(Chrome 114+). \n - Protocol drift: Commands like
setBlockedURLsare deprecated in favor ofsetRequestInterceptionwith URL patterns. Pin Chrome version in CI or use a library that abstracts version differences. \n - Headless bug (pre‑112):
requestInterceptedcould fire twice for redirected requests. Upgrade or deduplicate byinterceptionId. \n - WebSocket/WebRTC blind spot: The Network domain cannot intercept WebSocket frames or WebRTC traffic; those require separate CDP domains with more limited APIs. \n
Verify It Works Before You Depend On It
\nLaunch Chrome with google-chrome --remote-debugging-port=9222 --headless=new, connect via WebSocket, send {id:1, method:'Network.enable'} followed by {id:2, method:'Network.setRequestInterception', params:{patterns:[{urlPattern:'*api.example.com*', resourceType:'XHR'}]}}. Visit a page that calls the matched API and observe the requestIntercepted event. Call continueInterceptedRequest with modified headers and confirm they appear server‑side.
For ongoing reliability, check command compatibility at chromedevtools.github.io/devtools-protocol for your Chrome major version. Test timeout behavior by deliberately not calling continueInterceptedRequest and confirming the request fails after ~30 seconds.
Bottom line: CDP Network interception is a stable, powerful automation surface — treat it like any critical dependency. Pin versions, guard timeouts, and keep patterns narrow.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.