diff --git a/cypress/integration/middleware/standard.spec.ts b/cypress/integration/middleware/standard.spec.ts
index 97387b7ca0..780f9f0c85 100644
--- a/cypress/integration/middleware/standard.spec.ts
+++ b/cypress/integration/middleware/standard.spec.ts
@@ -37,6 +37,14 @@ describe('Standard middleware', () => {
cy.getCookie('netlifyCookie').should('have.property', 'value', 'true')
})
})
+
+ // https://github.com/netlify/pillar-support/issues/350
+ it('MiddlewareResponse adds cookies', () => {
+ cy.request('/cookies/middleware').then((response) => {
+ cy.getCookie('middlewareCookie').should('have.property', 'value', 'true')
+ expect(response.headers).to.have.property('x-foo', 'bar')
+ })
+ })
})
describe('Middleware matchers', () => {
diff --git a/demos/middleware/middleware.ts b/demos/middleware/middleware.ts
index d0c7841997..5950c98671 100644
--- a/demos/middleware/middleware.ts
+++ b/demos/middleware/middleware.ts
@@ -53,6 +53,13 @@ export async function middleware(req: NextRequest) {
return request.rewrite('/api/hello')
}
+ if (pathname.startsWith('/cookies/middleware')) {
+ const response = await new MiddlewareRequest(req).next()
+ response.cookies.set('middlewareCookie', 'true')
+ response.headers.set('x-foo', 'bar')
+ return response
+ }
+
if (pathname.startsWith('/cookies')) {
response = NextResponse.next()
response.cookies.set('netlifyCookie', 'true')
@@ -129,8 +136,8 @@ export const config = {
matcher: [
'/api/:all*',
'/headers',
+ '/cookies/:path*',
{ source: '/static' },
- { source: '/cookies' },
{ source: '/matcher-cookie'},
{ source: '/shows/((?!99|88).*)' },
{
diff --git a/demos/middleware/pages/cookies/middleware.js b/demos/middleware/pages/cookies/middleware.js
new file mode 100644
index 0000000000..15f61a1b36
--- /dev/null
+++ b/demos/middleware/pages/cookies/middleware.js
@@ -0,0 +1,9 @@
+const Cookies = () => {
+ return (
+
+
The cookie "middlewareCookie" should be set to true
+
+ )
+}
+
+export default Cookies
diff --git a/packages/next/src/middleware/response.ts b/packages/next/src/middleware/response.ts
index 392171bec0..2e3eb519b9 100644
--- a/packages/next/src/middleware/response.ts
+++ b/packages/next/src/middleware/response.ts
@@ -11,7 +11,15 @@ export class MiddlewareResponse extends NextResponse {
private readonly dataTransforms: NextDataTransform[]
private readonly elementHandlers: Array<[selector: string, handlers: ElementHandlers]>
constructor(public originResponse: Response) {
- super()
+ // we need to propagate the set-cookie header, so response.cookies.get works correctly
+ const initHeaders = new Headers()
+ if (originResponse.headers.has('set-cookie')) {
+ initHeaders.set('set-cookie', originResponse.headers.get('set-cookie'))
+ }
+
+ super(undefined, {
+ headers: initHeaders,
+ })
// These are private in Node when compiling, but we access them in Deno at runtime
Object.defineProperty(this, 'dataTransforms', {
diff --git a/packages/runtime/src/templates/edge-shared/utils.ts b/packages/runtime/src/templates/edge-shared/utils.ts
index c1e5004168..35dbd3bb5a 100644
--- a/packages/runtime/src/templates/edge-shared/utils.ts
+++ b/packages/runtime/src/templates/edge-shared/utils.ts
@@ -57,10 +57,15 @@ export const addMiddlewareHeaders = async (
return response
}
+interface ResponseCookies {
+ readonly _headers: Headers
+}
+
interface MiddlewareResponse extends Response {
originResponse: Response
dataTransforms: NextDataTransform[]
elementHandlers: Array<[selector: string, handlers: ElementHandlers]>
+ get cookies(): ResponseCookies
}
interface MiddlewareRequest {
@@ -184,6 +189,12 @@ export const buildResponse = async ({
if (request.method === 'HEAD' || request.method === 'OPTIONS') {
return response.originResponse
}
+
+ // NextResponse doesn't set cookies onto the originResponse, so we need to copy them over
+ if (response.cookies._headers.has('set-cookie')) {
+ response.originResponse.headers.set('set-cookie', response.cookies._headers.get('set-cookie')!)
+ }
+
// If it's JSON we don't need to use the rewriter, we can just parse it
if (response.originResponse.headers.get('content-type')?.includes('application/json')) {
const props = await response.originResponse.json()