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
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=webkitThe repro starts a local server with two routes:
/requires Basic auth,/apiechoesback the
Authorizationheader it received. A context is created withhttpCredentials, the page loads/, then callsfetch("/api")with its ownAuthorization: Bearer my-own-app-token.Expected behavior
Both browsers send the header the page set, so
/apireceivesBearer my-own-app-token.httpCredentialsshould only answer a401challenge, notreplace an
Authorizationheader the request already has.Actual behavior
Chromium passes, WebKit fails:
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
httpCredentialsfor a staging Basic auth, so the app behaves as if theuser 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 itsnetwork stack then keeps them for that origin. Later requests get the Basic header
applied even when they already carry an
Authorizationof their own, which is what thetest observes on
/api. That would explain why an app loses its own token after thefirst authenticated navigation.
Workaround: drop
httpCredentialsand answer the challenge incontext.route()instead, adding the Basic header only to requests that carry no
Authorizationoftheir 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