Playwright tests can be routed through HTTP proxies, allowing you to simulate network conditions, inspect traffic, or access resources behind a firewall.

Let’s see this in action. Imagine you’re testing a web application that requires access to an internal API. Without a proxy, your tests might fail if they can’t reach that API from your testing environment. By configuring Playwright to use an HTTP proxy, you can direct your test traffic through a machine that can access the internal API.

Here’s a sample playwright.config.ts demonstrating how to set up proxy configurations:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    // ... other configurations
    launchOptions: {
      // For Chromium, Firefox, and WebKit
      args: [
        '--proxy-server=http://127.0.0.1:8080', // Direct traffic through this proxy
        '--ignore-certificate-errors', // Often needed for internal proxies
      ],
    },
  },
  // You can also set environment variables for more dynamic configurations
  globalSetup: './global-setup', // Example: load proxy settings from env vars
});

In this example, args: ['--proxy-server=http://127.0.0.1:8080'] tells the browser to route all network requests through the proxy running on localhost:8080. The --ignore-certificate-errors flag is frequently necessary when dealing with proxies that use self-signed certificates or custom certificate authorities, common in enterprise environments.

The globalSetup option provides a way to dynamically configure Playwright, for instance, by reading proxy settings from environment variables. This is useful for CI/CD pipelines where proxy details might change.

// global-setup.ts
import { FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  const proxyUrl = process.env.PLAYWRIGHT_PROXY_URL;
  if (proxyUrl) {
    config.use.launchOptions.args.push(`--proxy-server=${proxyUrl}`);
    config.use.launchOptions.args.push('--ignore-certificate-errors'); // If needed
  }
}

export default globalSetup;

This global-setup.ts script reads PLAYWRIGHT_PROXY_URL from the environment. If it’s set, it injects the proxy arguments into Playwright’s launch options.

Playwright’s proxy capabilities extend beyond just routing traffic. You can intercept and modify requests or responses. This is achieved through the page.route() API.

Consider this test:

import { test, expect } from '@playwright/test';

test('intercept and modify request', async ({ page }) => {
  await page.goto('https://example.com');

  await page.route('**/*', async route => {
    const request = route.request();
    const headers = request.headers();
    headers['x-custom-header'] = 'playwright-proxy-test';
    await route.continue({ headers });
  });

  // Now, any request made by the page will have the custom header.
  // You can then assert on the modified request if you have a way to inspect it,
  // or observe the effect of the modification on the page's behavior.
  await page.waitForLoadState('networkidle');
  expect(true).toBe(true); // Placeholder for actual assertion
});

Here, page.route('**/*') intercepts all network requests. Inside the handler, we access the original request, modify its headers by adding x-custom-header, and then route.continue() sends the modified request onward. This is powerful for injecting authentication tokens, modifying user agents, or simulating specific server responses.

The core problem this solves is isolating your tests from the actual network, allowing for controlled environments. You can simulate slow networks, block certain domains, or even redirect traffic to a mock server. The launchOptions.args are the most direct way to configure the browser’s built-in proxy settings, while page.route() provides granular control over individual requests and responses after the browser has started and is making network calls.

The args approach configures the browser before it makes any requests. The page.route API, on the other hand, operates at a higher level within Playwright’s context, allowing for dynamic interception and modification of requests initiated by the page itself. This means you can apply page.route rules after the browser has launched and even after some requests have already been made, as long as they haven’t completed.

When using page.route to modify requests, the route.continue() method has several useful parameters. You can change the URL, headers, method, or even the post data. This allows for complex scenarios like seamlessly redirecting a request to a different endpoint or injecting dynamic data into a POST body.

The next challenge is often handling authenticated proxies, which require credentials.

Want structured learning?

Take the full Playwright course →