Skip to main content

Using man-in-the-middle proxy to intercept requests in Puppeteer

Sometimes you may need to intercept (or maybe block) requests in headless Chrome / Puppeteer, but page.setRequestInterception()  is not 100% reliable when the request is started in a new window.

One possible way to intercept these requests is to use a man-in-the-middle (MITM) proxy, i.e. a proxy server that can intercept and modify HTTP requests, even those over HTTPS. In this example, we're going to use, since it has all the tools that we need.

First we set up the MITM proxy:

const { promisify } = require('util');
const { exec } = require('child_process');
const Proxy = require('http-mitm-proxy');
const Promise = require('bluebird');

const execPromise = promisify(exec);

const wait = (timeout) => new Promise((resolve) => setTimeout(resolve, timeout));

const setupProxy = async (port) => {
// Setup chromium certs directory
// WARNING: this only works in debian docker images
// modify it for any other use cases or local usage.
await execPromise('mkdir -p $HOME/.pki/nssdb');
await execPromise('certutil -d sql:$HOME/.pki/nssdb -N');
const proxy = Proxy();
return new Promise((resolve, reject) => {
proxy.listen({ port, silent: true }, (err) => {
if (err) return reject(err);
// Add CA certificate to chromium and return initialize proxy object
execPromise('certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n mitm-ca -i ./.http-mitm-proxy/certs/ca.pem')
.then(() => resolve(proxy))

Then we'll need a Docker image that has the certutil utility. Here is an example of a Dockerfile that can create such an image and is based on the apify/actor-node-chrome image that contains Puppeteer.

Now we need to specify how the proxy shall handle the intercepted requests:

// Setup blocking of requests in proxy
const proxyPort = 8000;
const proxy = setupProxy(proxyPort);
proxy.onRequest((context, callback) => {
if (blockRequests) {
const request = context.clientToProxyRequest;
// Log out blocked requests
console.log('Blocked request:',, request.url);

// Close the connection with custom content
return callback();

The final step is to let Puppeteer use the local proxy:

// Launch puppeteer with local proxy
const browser = await puppeteer.launch({
args: ['--no-sandbox', `--proxy-server=localhost:${proxyPort}`],

And we're done! By adjusting the blockRequests variable, you can allow or block any request initiated through Puppeteer.

Here is a GitHub repository with a full example and all necessary files:

If you have any questions, feel free to contact us in the chat.

Happy intercepting!