microsoft/playwright · error · Error
"referer" is already specified as extra HTTP header
Error message
"referer" is already specified as extra HTTP header
What it means
page.goto(url, { referer }) conflicts with an extraHTTPHeaders 'referer' already set on the page/context. If both are present and differ, goto throws rather than silently letting one win.
Source
Thrown at packages/playwright-core/src/server/frames.ts:704
};
this._redirectedNavigations.set(documentId, data);
data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId));
}
async goto(progress: Progress, url: string, options: types.GotoOptions = {}): Promise<network.Response | null> {
const constructedNavigationURL = constructURLBasedOnBaseURL(this._page.browserContext._options.baseURL, url);
return this.raceNavigationAction(progress, async () => this.gotoImpl(progress, constructedNavigationURL, options));
}
async gotoImpl(progress: Progress, url: string, options: types.GotoOptions): Promise<network.Response | null> {
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
progress.log(`navigating to "${url}", waiting until "${waitUntil}"`);
const headers = this._page.extraHTTPHeaders() || [];
const refererHeader = headers.find(h => h.name.toLowerCase() === 'referer');
let referer = refererHeader ? refererHeader.value : undefined;
if (options.referer !== undefined) {
if (referer !== undefined && referer !== options.referer)
throw new Error('"referer" is already specified as extra HTTP header');
referer = options.referer;
}
url = helper.completeUserURL(url);
const navigationEvents: NavigationEvent[] = [];
const collectNavigations = (arg: NavigationEvent) => navigationEvents.push(arg);
this.on(Frame.Events.InternalNavigation, collectNavigations);
let navigateResult;
try {
navigateResult = await progress.race(this._page.delegate.navigateFrame(this, url, referer));
} finally {
this.off(Frame.Events.InternalNavigation, collectNavigations);
}
let event: NavigationEvent;
if (navigateResult.newDocumentId) {
const predicate = (event: NavigationEvent) => {
// We are interested either in this specific document, or any other document thatView on GitHub (pinned to c8fc3bf8d3)
Solutions
- Remove the referer from extraHTTPHeaders if you intend to set it per-goto.
- Or stop passing options.referer and rely solely on the extra header.
- Ensure the two values are identical if both must be set.
Example fix
// before
const ctx = await browser.newContext({ extraHTTPHeaders: { referer: 'https://a.test' } });
await page.goto(url, { referer: 'https://b.test' }); // mismatch -> throws
// after: pick one source of truth
await page.goto(url, { referer: 'https://b.test' }); // and drop the extra header Defensive patterns
Strategy: validation
Validate before calling
// Reconcile referer sources before goto
const headers = page.context().options()?.extraHTTPHeaders ?? {};
const ctxReferer = headers['referer'] ?? headers['Referer'];
if (ctxReferer && gotoReferer && ctxReferer !== gotoReferer) {
throw new Error('referer conflict');
} Type guard
null
Try / catch
null
Prevention
- Choose a single referer strategy (context-level OR per-goto), not both.
- When refactoring, search for 'referer' case-insensitively in header config.
When it happens
Trigger: Setting extraHTTPHeaders with a 'referer' (case-insensitive) on the context or page, and then also passing options.referer to page.goto with a different value.
Common situations: Reusing a context that sets a global referer for telemetry and then overriding per-navigation; migrating from context-level referer to per-goto referer without removing the header; case variants ('Referer' vs 'referer').
Related errors
- ${name}: expected one of (load|domcontentloaded|networkidle|
- Invalid input image
- Invalid output dimensions
- Error while parsing selector `${selector}` - selector cannot
- Cannot find .trace file
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/3be568ff4b1a197a.
Report an issue: GitHub.