diff --git a/packages/nuxt/src/app/composables/cookie.ts b/packages/nuxt/src/app/composables/cookie.ts
index a05773cd79..3e3a5dfd10 100644
--- a/packages/nuxt/src/app/composables/cookie.ts
+++ b/packages/nuxt/src/app/composables/cookie.ts
@@ -2,7 +2,7 @@ import type { Ref } from 'vue'
import { ref, watch } from 'vue'
import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
import { parse, serialize } from 'cookie-es'
-import { appendResponseHeader } from 'h3'
+import { deleteCookie, getCookie, setCookie } from 'h3'
import type { H3Event } from 'h3'
import destr from 'destr'
import { isEqual } from 'ohash'
@@ -80,7 +80,16 @@ function writeClientCookie (name: string, value: any, opts: CookieSerializeOptio
function writeServerCookie (event: H3Event, name: string, value: any, opts: CookieSerializeOptions = {}) {
if (event) {
- // TODO: Try to smart join with existing Set-Cookie headers
- appendResponseHeader(event, 'Set-Cookie', serializeCookie(name, value, opts))
+ // update if value is set
+ if (value !== null && value !== undefined) {
+ return setCookie(event, name, value, opts)
+ }
+
+ // delete if cookie exists in browser and value is null/undefined
+ if (getCookie(event, name) !== undefined) {
+ return deleteCookie(event, name, opts)
+ }
+
+ // else ignore if cookie doesn't exist in browser and value is null/undefined
}
}
diff --git a/test/basic.test.ts b/test/basic.test.ts
index c44f5569d0..656d3ab5cc 100644
--- a/test/basic.test.ts
+++ b/test/basic.test.ts
@@ -399,6 +399,21 @@ describe('nuxt composables', () => {
const html = await $fetch('/url')
expect(html).toContain('path: /url')
})
+ it('sets cookies correctly', async () => {
+ const res = await fetch('/cookies', {
+ headers: {
+ cookie: Object.entries({
+ 'browser-accessed-but-not-used': 'provided-by-browser',
+ 'browser-accessed-with-default-value': 'provided-by-browser',
+ 'browser-set': 'provided-by-browser',
+ 'browser-set-to-null': 'provided-by-browser',
+ 'browser-set-to-null-with-default': 'provided-by-browser'
+ }).map(([key, value]) => `${key}=${value}`).join('; ')
+ }
+ })
+ const cookies = res.headers.get('set-cookie')
+ expect(cookies).toMatchInlineSnapshot('"set-in-plugin=true; Path=/, set=set; Path=/, browser-set=set; Path=/, browser-set-to-null=; Max-Age=0; Path=/, browser-set-to-null-with-default=; Max-Age=0; Path=/"')
+ })
})
describe('rich payloads', () => {
diff --git a/test/fixtures/basic/pages/cookies.vue b/test/fixtures/basic/pages/cookies.vue
new file mode 100644
index 0000000000..45c67460df
--- /dev/null
+++ b/test/fixtures/basic/pages/cookies.vue
@@ -0,0 +1,19 @@
+
+
+
+
+