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.

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@f1d33b5029 (2026-08-12). Data as JSON: /api/errors/3be568ff4b1a197a. Report an issue: GitHub.

Appendix: source

Thrown at packages/playwright-core/src/server/frames.ts:702

    };
    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 f1d33b5029)