Skip to content

[Bug]: WebKit replaces a request's own Authorization header with httpCredentials #42291

Description

Version

1.62.1

Steps to reproduce

Repro: https://github.com/hornc-greedy/playwright-webkit-httpcredentials-repro

npm install
npx playwright install chromium webkit
npx playwright test --project=chromium --project=webkit

The repro starts a local server with two routes: / requires Basic auth, /api echoes
back the Authorization header it received. A context is created with
httpCredentials, the page loads /, then calls fetch("/api") with its own
Authorization: Bearer my-own-app-token.

Expected behavior

Both browsers send the header the page set, so /api receives
Bearer my-own-app-token. httpCredentials should only answer a 401 challenge, not
replace an Authorization header the request already has.

Actual behavior

Chromium passes, WebKit fails:

  ✓  1 [chromium] › httpcredentials-overwrite.spec.ts:5:5 (55ms)
  ✘  2 [webkit] › httpcredentials-overwrite.spec.ts:5:5 (114ms)

    Error: expect(received).toBe(expected)

    Expected: "Bearer my-own-app-token"
    Received: "Basic dXNlcjpwYXNz"

The effect is not limited to the test's own fetch: on a site that authenticates its
users with a bearer token, every request the app sends loses its token as soon as the
context uses httpCredentials for a staging Basic auth, so the app behaves as if the
user were logged out.

Additional context

Also reproduced on Linux in the official image with the same version,
mcr.microsoft.com/playwright:v1.62.1-noble, so it is not macOS-specific.

A guess at the mechanism, not a finding: the navigation to / is answered with a 401,
WebKit answers the challenge with the credentials from httpCredentials, and its
network stack then keeps them for that origin. Later requests get the Basic header
applied even when they already carry an Authorization of their own, which is what the
test observes on /api. That would explain why an app loses its own token after the
first authenticated navigation.

Workaround: drop httpCredentials and answer the challenge in context.route()
instead, adding the Basic header only to requests that carry no Authorization of
their own. Note that requests served by a Service Worker may not reach
context.route() at all.

Environment

System:
    OS: macOS 26.5.2
    CPU: (11) arm64 Apple M3 Pro
    Memory: 135.30 MB / 36.00 GB
  Binaries:
    Node: 26.2.0 - /.nvm/versions/node/v26.2.0/bin/node
    npm: 11.13.0 - /.nvm/versions/node/v26.2.0/bin/npm
  IDEs:
    VSCode: 1.124.0 - /usr/local/bin/code
  Languages:
    Bash: 3.2.57 - /bin/bash
  npmPackages:
    @playwright/test: ^1.62.1 => 1.62.1

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions