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 that

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Remove the referer from extraHTTPHeaders if you intend to set it per-goto.
  2. Or stop passing options.referer and rely solely on the extra header.
  3. 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

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


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/3be568ff4b1a197a. Report an issue: GitHub.