Version
1.64.0-next (main @ 04e547b); also reproduces on 1.63.0
Steps to reproduce
const http = require('http');
const { request } = require('playwright');
const server = http.createServer((req, res) => {
console.log(req.url, req.headers.cookie);
if (req.url === '/redirect') {
res.writeHead(302, { location: '/final' });
return res.end();
}
res.end('ok');
});
server.listen(0, '127.0.0.1', async () => {
const base = `http://127.0.0.1:${server.address().port}`;
const context = await request.newContext({ extraHTTPHeaders: { cookie: 'token=xyz' } });
await context.get(base + '/redirect');
await context.get(base + '/redirect', { headers: { cookie: 'token=abc' } });
await context.dispose();
server.close();
});
Expected behavior
The cookie header is sent on the redirected request as well, the same as every other header passed through headers or extraHTTPHeaders. The documentation for the headers option says: "These headers will apply to the fetched request as well as any redirects initiated by it."
/redirect token=xyz
/final token=xyz
/redirect token=abc
/final token=abc
Actual behavior
The redirect target receives no cookie at all:
/redirect token=xyz
/final undefined
/redirect token=abc
/final undefined
When the context cookie store has cookies for the target URL, those replace the explicit header on the redirected request instead.
Additional context
_sendRequest in packages/playwright-core/src/server/fetch.ts adds cookies from the context store to options.headers unless a cookie header is already present, so an explicit header wins on the first hop. The redirect path then removes cookie from the headers unconditionally before following the redirect, because it cannot tell the caller's header apart from the ones it added itself. The next hop only gets whatever the cookie store holds for the new URL.
This affects playwright.request.newContext(), context.request and route.fetch(). A common case is a standalone request context with a session cookie copied from a browser and an endpoint that redirects, for example /login to /dashboard or /api/resource to /api/resource/: the second request goes out unauthenticated, and the failure is silent.
I intend to work on this and will send a PR.
Environment
- Operating System: Windows 11
- Node.js: 22.17.0
- Playwright: main @ 04e547b
Version
1.64.0-next (main @ 04e547b); also reproduces on 1.63.0
Steps to reproduce
Expected behavior
The
cookieheader is sent on the redirected request as well, the same as every other header passed throughheadersorextraHTTPHeaders. The documentation for theheadersoption says: "These headers will apply to the fetched request as well as any redirects initiated by it."Actual behavior
The redirect target receives no cookie at all:
When the context cookie store has cookies for the target URL, those replace the explicit header on the redirected request instead.
Additional context
_sendRequestinpackages/playwright-core/src/server/fetch.tsadds cookies from the context store tooptions.headersunless acookieheader is already present, so an explicit header wins on the first hop. The redirect path then removescookiefrom the headers unconditionally before following the redirect, because it cannot tell the caller's header apart from the ones it added itself. The next hop only gets whatever the cookie store holds for the new URL.This affects
playwright.request.newContext(),context.requestandroute.fetch(). A common case is a standalone request context with a session cookie copied from a browser and an endpoint that redirects, for example/loginto/dashboardor/api/resourceto/api/resource/: the second request goes out unauthenticated, and the failure is silent.I intend to work on this and will send a PR.
Environment