diff --git a/packages/playwright-core/src/server/cookieStore.ts b/packages/playwright-core/src/server/cookieStore.ts index b1ed6ecaa2..fa93759ebc 100644 --- a/packages/playwright-core/src/server/cookieStore.ts +++ b/packages/playwright-core/src/server/cookieStore.ts @@ -28,7 +28,7 @@ class Cookie { // https://datatracker.ietf.org/doc/html/rfc6265#section-5.4 matches(url: URL): boolean { - if (this._raw.secure && url.protocol !== 'https:') + if (this._raw.secure && (url.protocol !== 'https:' && url.hostname !== 'localhost')) return false; if (!domainMatches(url.hostname, this._raw.domain)) return false; diff --git a/packages/playwright-core/src/server/network.ts b/packages/playwright-core/src/server/network.ts index 5c6ca29abe..800788e19e 100644 --- a/packages/playwright-core/src/server/network.ts +++ b/packages/playwright-core/src/server/network.ts @@ -36,7 +36,7 @@ export function filterCookies(cookies: types.NetworkCookie[], urls: string[]): t continue; if (!parsedURL.pathname.startsWith(c.path)) continue; - if (parsedURL.protocol !== 'https:' && c.secure) + if (parsedURL.protocol !== 'https:' && parsedURL.hostname !== 'localhost' && c.secure) continue; return true; } diff --git a/tests/browsercontext-fetch.spec.ts b/tests/browsercontext-fetch.spec.ts index dea09fe655..3dd1991601 100644 --- a/tests/browsercontext-fetch.spec.ts +++ b/tests/browsercontext-fetch.spec.ts @@ -900,6 +900,19 @@ it('context request should export same storage state as context', async ({ conte expect(pageState).toEqual(contextState); }); +it('should send secure cookie over http for localhost', async ({ page, server }) => { + server.setRoute('/setcookie.html', (req, res) => { + res.setHeader('Set-Cookie', ['a=v; secure']); + res.end(); + }); + await page.request.get(`${server.PREFIX}/setcookie.html`); + const [serverRequest] = await Promise.all([ + server.waitForRequest('/empty.html'), + page.request.get(server.EMPTY_PAGE) + ]); + expect(serverRequest.headers.cookie).toBe('a=v'); +}); + it('should accept bool and numeric params', async ({ page, server }) => { let request; const url = new URL(server.EMPTY_PAGE); diff --git a/tests/global-fetch-cookie.spec.ts b/tests/global-fetch-cookie.spec.ts index fa3e09097f..6c6fdf44c2 100644 --- a/tests/global-fetch-cookie.spec.ts +++ b/tests/global-fetch-cookie.spec.ts @@ -138,6 +138,19 @@ it('should send secure cookie over https', async ({ request, server, httpsServer expect(serverRequest.headers.cookie).toBe('a=v; b=v'); }); +it('should send secure cookie over http for localhost', async ({ request, server }) => { + server.setRoute('/setcookie.html', (req, res) => { + res.setHeader('Set-Cookie', ['a=v; secure', 'b=v']); + res.end(); + }); + await request.get(`${server.PREFIX}/setcookie.html`); + const [serverRequest] = await Promise.all([ + server.waitForRequest('/empty.html'), + request.get(server.EMPTY_PAGE) + ]); + expect(serverRequest.headers.cookie).toBe('a=v; b=v'); +}); + it('should send not expired cookies', async ({ request, server }) => { server.setRoute('/setcookie.html', (req, res) => { const tomorrow = new Date();