- Playwright server-side mocking requires a different approach — page.route() only intercepts browser requests, not outgoing server calls.
- Using mockttp as a forward proxy solves Playwright server-side mocking without polluting application code with test-only branches.
- The HTTP_PROXY and HTTPS_PROXY environment variables are honoured by almost every HTTP client, making this approach language-agnostic.
- Spawning the dev server from a Playwright worker fixture — not webServer config — is critical for per-worker proxy configuration to work.
- Playwright server-side mocking requires a different approach — page.route() only intercepts browser requests, not outgoing server calls.
- Using mockttp as a forward proxy solves Playwright server-side mocking without polluting application code with test-only branches.
- The HTTP_PROXY and HTTPS_PROXY environment variables are honoured by almost every HTTP client, making this approach language-agnostic.
- Spawning the dev server from a Playwright worker fixture — not webServer config — is critical for per-worker proxy configuration to work.
Why Playwright Server-Side Mocking Is Harder Than It Looks
Playwright server-side mocking sounds like it should be a solved problem. You write an end-to-end test, you don’t want it hitting real third-party APIs, and you reach for the tools you know. The trouble is that page.route() — Playwright’s built-in request interception — only sees traffic flowing through the browser. The moment your application server makes its own outbound HTTP call to, say, OpenAI or a weather API, that request bypasses the browser entirely. page.route() never even knows it happened.
The workarounds most developers reach for first are also the ones they regret fastest. Sprinkling if (isUnderTest) conditionals through server code is a maintenance headache that grows with every new test scenario. Module-level mocking with tools like Jest’s module system works in unit tests but gets messy inside a running server process that Playwright is trying to drive end-to-end. What you really want is something that intercepts at the network boundary — something the application code doesn’t have to know about at all. That’s precisely the gap that effective Playwright server-side mocking needs to fill.
The answer is a forward proxy, and the open-source library mockttp is one of the cleanest ways to run one from inside your test suite.
How mockttp Turns a Forward Proxy Into a Test Fixture
mockttp spins up a local HTTP/HTTPS forward proxy in-process, generates a TLS certificate on the fly, and exposes a fluent API for defining exactly how specific requests should be answered. The trick that makes it universally useful for Playwright server-side mocking is the HTTP_PROXY and HTTPS_PROXY environment variable convention. Nearly every HTTP client in existence — from Node’s built-in fetch to Python’s requests library to Go’s net/http — honours these variables automatically. Point them at your mockttp instance and the proxy intercepts every outbound call your server makes, no code changes required.
Because Playwright runs tests across multiple workers in parallel, the right architecture is one mockttp server per worker. That keeps test isolation clean: worker A’s mocks don’t bleed into worker B’s test run. mockttp’s worker-scoped fixture pattern handles this neatly. Each worker generates its own CA certificate, writes it to a temporary file, and starts its own proxy instance. Unmatched requests — ones you haven’t explicitly mocked — pass through to the real internet by default, which means you’re not forced to mock every single dependency just to make your tests run.
The test-scoped mocks fixture sits on top of the worker-scoped server. At the start of each test, it hands your test direct access to the mockttp server. When the test finishes, it calls server.reset() to wipe the slate clean and re-registers the pass-through rule. This means each test starts with a completely fresh mock configuration — no accidental state leaking between scenarios. For a pattern that’s easy to mess up when rolling your own, getting it for free from a fixture is genuinely valuable. This clean-slate behaviour is one of the reasons mockttp has become a popular choice for Playwright server-side mocking specifically.
The Dev Server Problem — and Why webServer Config Won’t Cut It
Here’s a wrinkle that trips people up: Playwright ships a webServer config option that’s designed to boot your app before tests start. It’s convenient for simple setups. But it runs once, before any worker fixture has initialised, which means it has no idea what port your mockttp proxy is on, and no way to receive a per-worker CA certificate path. Your application server ends up running with none of the proxy environment variables set, and your mocks never fire. This is one of the most common reasons Playwright server-side mocking appears to be set up correctly but silently does nothing.
The fix is to give up on webServer for this use case and spawn the dev server from inside the worker fixture instead. That way the spawn call happens after mockttp is already running, and you can pass HTTPS_PROXY, HTTP_PROXY, NODE_EXTRA_CA_CERTS, and NODE_USE_ENV_PROXY directly into the child process’s environment. Setting PORT: “0” lets the OS assign a free port, which prevents worker collisions. The fixture then reads the actual port from the server’s stdout — watching for a line like Listening on https://localhost:XXXXX — and uses it to set baseURL so that page.goto(“/”) still works exactly as expected.
It’s a few extra lines compared to the webServer one-liner, but the control you get in return is worth it. Each worker owns its server process entirely, and the whole thing tears down cleanly when the worker finishes.
A Note on NODE_USE_ENV_PROXY
If you’re running Node 20 or later and using the platform’s built-in fetch, there’s one extra environment variable you need: NODE_USE_ENV_PROXY=1. Without it, Node’s native fetch ignores HTTPS_PROXY entirely — a gotcha that’s caught more than a few developers who expected Playwright server-side mocking to just work out of the box. Older HTTP libraries like node-fetch or axios tend to handle the proxy variables correctly on their own, but the built-in implementation needed an explicit opt-in until more recent Node releases started changing that behaviour.
This Isn’t Just a Node.js Solution
One of the more underappreciated aspects of the forward proxy approach to Playwright server-side mocking is how broadly it applies. The HTTP_PROXY convention isn’t Node-specific — it’s a Unix-era standard that’s been baked into HTTP clients across virtually every programming language and runtime. Python applications need SSL_CERT_FILE pointed at mockttp’s certificate alongside the proxy variables. Go applications use SSL_CERT_DIR. Ruby follows a similar pattern to Python. Rust’s reqwest crate picks up the proxy variables automatically. Even .NET honours them.
Java is the outlier, requiring -Dhttps.proxyHost and -Dhttps.proxyPort JVM flags rather than environment variables, but the underlying concept is identical. If your team runs a polyglot stack — a Node frontend server talking to a Go or Python microservice, for instance — you can apply the same mockttp proxy technique across every tier, from a single set of Playwright test fixtures.
What This Means for End-to-End Test Quality
The broader implication here is about what end-to-end tests are actually supposed to test. The whole point of E2E coverage is that the application behaves correctly as a system — not as a collection of individually-mocked units. But practical E2E testing has always had to make peace with external dependencies: you can’t run your CI pipeline against the real OpenAI API every time a developer pushes a commit.
The forward proxy pattern keeps the application completely unaware that it’s under test. There are no special branches, no injected test clients, no environment flags inside the application code itself. The server makes the same HTTPS calls it always makes. The proxy decides what to do with them. That fidelity matters: a test that exercises a realistic code path is worth significantly more than one that exercises a code path riddled with test-only shortcuts. Done properly, Playwright server-side mocking via a forward proxy produces tests that are both reliable and genuinely representative of production behaviour.
As AI-powered features — OpenAI integrations, embedding APIs, image generation endpoints — become standard parts of web applications, the ability to mock those calls reliably at the network layer is going to become a baseline expectation for serious test suites, not an advanced technique. mockttp and the forward proxy pattern are well-positioned to become a standard part of that toolkit.
Source: https://dev.to/playwright/mocking-server-side-http-in-playwright-with-mockttp-58jo

