Implementation:Microsoft Playwright Page RouteFromHAR
| Knowledge Sources | |
|---|---|
| Domains | Network_Testing, Mocking, HAR_Fixtures |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Concrete tool for recording and replaying HTTP responses from HAR files to create deterministic test fixtures, provided by the Playwright library.
Description
The page.routeFromHAR() method (packages/playwright-core/src/client/page.ts:L511-522) enables both recording and replaying of HTTP traffic using HAR (HTTP Archive) files. In replay mode, it intercepts network requests and fulfills them from a previously recorded HAR file. In recording mode, it captures live network traffic into a new HAR file for future replay.
The implementation involves three key components:
HarRouter (packages/playwright-core/src/client/harRouter.ts:L25-106) is the client-side router that integrates with Playwright's route system. Its _handle() method at L45-89 processes each intercepted request by:
- Extracting request properties (URL, method, headers, POST body, navigation status).
- Calling
localUtils.harLookup()to search the HAR file for a matching entry. - Handling the response action:
'redirect'triggers a navigation redirect,'fulfill'serves the recorded response viaroute.fulfill(), and'error'falls through to thenotFoundaction. - When no match is found, either aborting (
route.abort()) or falling back (route.fallback()) based on thenotFoundconfiguration.
HarBackend (packages/playwright-core/src/server/harBackend.ts:L28-157) is the server-side component that performs the actual HAR entry matching. Its _harFindResponse() method at L91-152 matches requests by URL, method, headers, and POST body against HAR entries.
HarRecorder (referenced at harRecorder.ts:L34-103) handles the recording phase, using HarTracer to capture live traffic and flush() at L73-96 to write the HAR output as either a plain JSON file or a ZIP archive with attached response bodies.
When options.update is true, the method delegates to browserContext._recordIntoHAR() instead of replaying, which sets up the HarRecorder to capture traffic.
Usage
Use page.routeFromHAR() when:
- You want to replay a previously recorded set of API responses for deterministic testing.
- You need to record API traffic from a real server to create test fixtures.
- You have many API endpoints and manually mocking each one with
page.route()would be impractical. - You want to create snapshot-based network tests that can be re-recorded when the API changes.
- You need to test offline scenarios by serving all network responses from a local HAR file.
Code Reference
Source Location
- Repository: playwright
- routeFromHAR:
packages/playwright-core/src/client/page.ts:L511-522 - HarRouter:
packages/playwright-core/src/client/harRouter.ts:L25-106 - HarBackend:
packages/playwright-core/src/server/harBackend.ts:L28-157
Signature
page.routeFromHAR(
har: string,
options?: {
url?: string | RegExp;
notFound?: 'abort' | 'fallback';
update?: boolean;
updateContent?: 'attach' | 'embed';
updateMode?: 'minimal' | 'full';
}
): Promise<void>;
Import
// Playwright Test (recommended)
import { test, expect } from '@playwright/test';
// Library mode
import { chromium } from 'playwright';
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| har | string |
Yes | Path to the HAR file. In replay mode, this file must exist and contain valid HAR data. In record mode (update: true), traffic will be written to this path.
|
| options.url | RegExp | No | URL pattern filter. Only requests matching this pattern will be served from the HAR file or recorded. Defaults to matching all requests (**/*).
|
| options.notFound | 'fallback' | No | Action when a matching HAR entry is not found. 'abort' (default) cancels the request. 'fallback' passes to the next route handler or the real network.
|
| options.update | boolean |
No | When true, records live network traffic into the HAR file instead of replaying. Used to create or refresh fixtures.
|
| options.updateContent | 'embed' | No | Controls how response bodies are stored in the HAR file during recording. 'attach' stores bodies as separate files in a ZIP archive (reduces HAR file size for binary content). 'embed' inlines bodies as base64 in the HAR JSON.
|
| options.updateMode | 'full' | No | Controls the level of detail in the recorded HAR. 'minimal' records only essential request/response data. 'full' records comprehensive details including timing.
|
Outputs
| Name | Type | Description |
|---|---|---|
| return | Promise<void> |
Resolves when the HAR route is registered (replay mode) or recording is set up (record mode). |
| Replay behavior | Implicit | Matching requests are fulfilled with recorded responses via route.fulfill(). Non-matching requests are handled per notFound setting.
|
| Recording output | HAR file | When update: true, a HAR file (or ZIP archive) is written to the specified path when the browser context closes, containing all captured network traffic.
|
Usage Examples
Basic Example: Replay from HAR File
import { test, expect } from '@playwright/test';
test('replay API responses from HAR', async ({ page }) => {
// Serve network requests from the recorded HAR file
await page.routeFromHAR('tests/fixtures/api-responses.har');
await page.goto('https://example.com/dashboard');
// The page will receive recorded responses instead of live API calls
await expect(page.locator('.user-count')).toHaveText('42');
});
Record HAR Fixture
import { test, expect } from '@playwright/test';
test('record API responses to HAR file', async ({ page }) => {
// Record live traffic into a HAR file
await page.routeFromHAR('tests/fixtures/api-responses.har', {
update: true,
url: '**/api/**', // Only record API calls
updateContent: 'attach', // Store bodies in a ZIP
updateMode: 'minimal',
});
await page.goto('https://example.com/dashboard');
await page.click('#load-more');
await page.waitForLoadState('networkidle');
// HAR file is written when the context closes
});
Filter by URL Pattern
import { test, expect } from '@playwright/test';
test('replay only specific API routes', async ({ page }) => {
// Only intercept /api/ requests; let other requests through
await page.routeFromHAR('tests/fixtures/api.har', {
url: '**/api/**',
notFound: 'fallback', // Let unmatched requests go to the network
});
await page.goto('https://example.com');
// Static assets load from the real server
// API calls are served from the HAR file
});
Fallback for Unmatched Requests
import { test, expect } from '@playwright/test';
test('HAR with fallback to network', async ({ page }) => {
// Use HAR for known responses, fall back to real network for new endpoints
await page.routeFromHAR('tests/fixtures/baseline.har', {
notFound: 'fallback',
});
await page.goto('https://example.com');
// Known endpoints: served from HAR
// New endpoints: fetched from real server
});
Combined with URL-Specific Regex Filter
import { test, expect } from '@playwright/test';
test('replay HAR for user endpoints only', async ({ page }) => {
await page.routeFromHAR('tests/fixtures/users-api.har', {
url: /\/api\/users/,
notFound: 'abort',
});
// Mock other endpoints manually
await page.route('**/api/config', route => {
route.fulfill({ json: { theme: 'dark', locale: 'en' } });
});
await page.goto('https://example.com/users');
});