Your Playwright suite tests a staging site behind HTTP Basic auth that also calls a partner API — how do you authenticate without leaking credentials?
answer
- One option answers a challenge
- The other always sends a header
- Pin credentials with the origin field
- Unpinned credentials answer any 401
- Read the values from the environment
basics
~20 sPut httpCredentials on the context and pin it with the origin field, so Basic auth is answered only for the staging host. Without an origin, the same username and password answer any 401, including the partner's.
solid answer
~40 sCreate the context with `httpCredentials: { username, password, origin: 'https://staging.hotels.example' }`. The browser then answers the staging host's Basic auth challenge for you, and because `origin` is set, a 401 from the partner API gets nothing. The documentation is explicit that with no origin the credentials go to any server that responds unauthorized — that is the leak. A static token is a different option, `extraHTTPHeaders`, which adds headers to **every** request any page in the context makes, to any host, so it is the blunter instrument; context headers merge with page-level ones and the page value wins on a clash. Read both from the environment, never from the repository. In Playwright 1.63 the credentials object also takes `send`, which only affects requests made through an API request context, not the browser's own.
code
typescript · 11 linesconst context = await browser.newContext({
httpCredentials: {
username: process.env.STAGING_USER!,
password: process.env.STAGING_PASS!,
origin: 'https://staging.hotels.example',
},
extraHTTPHeaders: { 'x-staging-bypass': process.env.STAGING_TOKEN! },
});
const page = await context.newPage();
await page.goto('https://staging.hotels.example/search');go deeper
Know that Basic auth in a test is configured on the context with httpCredentials rather than typed into a browser dialog.
Explain the difference between answering a 401 challenge and always sending a header, and what the origin field narrows.
Show the leak you are preventing: unpinned credentials answering any host's 401, and a context-wide header reaching third-party origins, plus where the secrets come from in CI.
Own the policy for test credentials across environments: which identities exist, how they are scoped and rotated, and what a suite is allowed to send to systems another team operates.
## Two different jobs, two different options Getting a request past a gate on staging is either a challenge-response or a static token, and Playwright has one context option for each. - `httpCredentials` answers an **HTTP Basic** (or Digest) challenge: the server replies 401 with a `WWW-Authenticate` header, and the browser retries with the credentials you configured. - `extraHTTPHeaders` adds fixed headers to **every request initiated by any page in the context** — the right tool for a staging bypass token or a tracing header, and the wrong tool for anything that must be scoped. ## Why `origin` is the important part With no `origin`, the documentation states the username and password are sent to *any* server on an unauthorized response. On a booking site that talks to a payment sandbox and a partner inventory API, that means your staging credentials are one 401 away from being handed to a third party you do not control. Setting `origin: 'https://staging.hotels.example'` restrains them to the host that issued the challenge. The origin is scheme, host and port — `https://staging.hotels.example` does not cover a plain-HTTP variant or a different port. | Option | Scope | Triggered by | Good for | |---|---|---|---| | `httpCredentials` | one origin when `origin` is set, otherwise any host | a 401 challenge | Basic auth on staging | | `extraHTTPHeaders` | every request from every page in the context | nothing, always sent | a static gate token | ## The `send` field is not about the browser `httpCredentials` also accepts `send: 'always' | 'unauthorized'`, defaulting to `'unauthorized'`. In Playwright 1.63 that field applies only to requests made through an API request context, not to the browser's own navigation and fetch traffic, so reaching for it to "always send the header in the browser" is a misreading. If you genuinely need an `Authorization` header on browser requests regardless of a challenge, build it yourself as an entry in `extraHTTPHeaders`. ## Handling the extra headers safely 1. Keep the header list as small as the gate requires — a token added for the front door is also sent to analytics, fonts and any third-party origin the page touches. 2. Remember the merge rule: context headers combine with headers set on a page, and the page value wins for a header set in both places. 3. Header order is explicitly not guaranteed, so never assert on it. ## Where the secrets come from Read both the credentials and the token from environment variables provided by the CI secret store, and keep them out of the repository and out of any committed config. A traced run is worth a thought too: a header that appears in every request will also appear in artifacts you attach to a failed run, which is a good reason to keep the gate token scoped to a non-production environment. ## What about a bad certificate on the same box? Staging boxes that need Basic auth often also carry a self-signed certificate, and `ignoreHTTPSErrors` tempts you to switch it on for the whole suite. Treat it as a separate decision: it applies to every host the context talks to, and it hides genuine certificate regressions along with the one you meant to tolerate. ## Diagnosing it in practice - Every request returns 401 and no credentials were configured: the option is missing, or it was set on the wrong context. - The staging host works but the partner call now fails with an authentication error on their side: your credentials were sent to them, which is exactly what `origin` prevents. - The gate works locally and fails in CI: the environment variable is not set on the runner, and an empty string was passed silently.
- When is extraHTTPHeaders the wrong way to add an Authorization header?Whenever the value must not reach every host. Context headers go out with every request any page makes, including third-party origins, so a bearer token added this way is broadcast. Scope-sensitive credentials belong in `httpCredentials` with an `origin`, or in a per-request mechanism.
- A header set on both the context and the page has two different values — which one is sent?The page value. Context-level extra headers are merged with page-level ones, and a header set on the page overrides the context's value for requests that page makes. Header ordering, by contrast, is explicitly not guaranteed, so no assertion should depend on it.
saying these in an interview costs you the question
- Sets httpCredentials with no origin on a shared environment
- Uses extraHTTPHeaders for a token that must stay scoped
- Thinks send always applies to browser requests
- Commits staging credentials into the config file
- Assumes header order in outgoing requests is stable