Skip to content

Commit ddd5ae5

Browse files
authored
fix: Make locale cookie a session cookie (#1634)
Initially, I planned to add an expiration of 5 hours to the locale cookie for `next-intl@4` to comply with GDPR regulations. However, this has the implication that if the browser remains open for longer than 5 hours, the cookie can be reset in the middle of a session. Due to this, it seems more reasonable to not set an expiration at all, turning the cookie into a session cookie. Session cookies expiry only when a browser is closed. On mobile, this can be even more beneficial, as browsers are rarely closed (the browser can clear cookies though if memory is constrained).
1 parent e65f4f9 commit ddd5ae5

File tree

4 files changed

+7
-16
lines changed

4 files changed

+7
-16
lines changed

docs/src/pages/docs/routing.mdx

+3-4
Original file line numberDiff line numberDiff line change
@@ -473,11 +473,10 @@ In this case, only the locale prefix and a potentially [matching domain](#domain
473473

474474
### Locale cookie [#locale-cookie]
475475

476-
If a user changes the locale to a value that doesn't match the `accept-language` header, `next-intl` will set a cookie called `NEXT_LOCALE` that contains the most recently detected locale. This is used to [remember the user's locale](/docs/routing/middleware#locale-detection) preference for future requests.
476+
If a user changes the locale to a value that doesn't match the `accept-language` header, `next-intl` will set a session cookie called `NEXT_LOCALE` that contains the most recently detected locale. This is used to [remember the user's locale](/docs/routing/middleware#locale-detection) preference for subsequent requests.
477477

478478
By default, the cookie will be configured with the following attributes:
479479

480-
1. [**`maxAge`**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#max-agenumber): This value is set to 5 hours in order to be [GDPR-compliant](#locale-cookie-gdpr) out of the box.
481480
2. [**`sameSite`**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value): This value is set to `lax` so that the cookie can be set when coming from an external site.
482481
3. [**`path`**](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#pathpath-value): This value is not set by default, but will use the value of your [`basePath`](#base-path) if configured.
483482

@@ -514,9 +513,9 @@ export const routing = defineRouting({
514513
<Details id="locale-cookie-gdpr">
515514
<summary>Which `maxAge` value should I consider for GDPR compliance?</summary>
516515

517-
The [Article 29 Working Party opinion 04/2012](https://ec.europa.eu/justice/article-29/documentation/opinion-recommendation/files/2012/wp194_en.pdf) provides a guideline for the expiration of cookies that are used to remember the user's language in section 3.6 "UI customization cookies".
516+
The [Article 29 Working Party opinion 04/2012](https://ec.europa.eu/justice/article-29/documentation/opinion-recommendation/files/2012/wp194_en.pdf) provides a guideline for the expiration of cookies that are used to remember the user's language in section 3.6 "UI customization cookies". In this policy, a language preference cookie set as a result of an explicit user action, such as using a language switcher, is allowed to remain active for "a few additional hours" after a browser session has ended.
518517

519-
In this policy, a language preference cookie set as a result of an explicit user action, such as using a language switcher, is allowed to remain active for "a few additional hours" after a browser session has ended. To be compliant out of the box, `next-intl` sets the `maxAge` value of the cookie to 5 hours.
518+
To be compliant out of the box, `next-intl` does not set the `max-age` value of the cookie, making it a session cookie that expires when a browser is closed.
520519

521520
However, the Working Party also states that if additional information about the use of cookies is provided in a prominent location (e.g. a "uses cookies" notice next to the language switcher), the cookie can be configured to remember the user's preference for "a longer duration". If you're providing such a notice, you can consider increasing `maxAge` accordingly.
522521

examples/example-app-router/tests/main.spec.ts

-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ it("sets a cookie when requesting a locale that doesn't match the `accept-langua
9595
expect(value).toContain('NEXT_LOCALE=de;');
9696
expect(value).toContain('Path=/;');
9797
expect(value).toContain('SameSite=lax');
98-
expect(value).toContain('Max-Age=18000;');
99-
expect(value).toContain('Expires=');
10098
});
10199

102100
it('serves a robots.txt', async ({page}) => {

packages/next-intl/src/navigation/react-client/createNavigation.test.tsx

+3-8
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,8 @@ describe("localePrefix: 'always', with `localeCookie`", () => {
273273
expect(cookieSpy).toHaveBeenCalledWith(
274274
[
275275
'NEXT_LOCALE=de',
276-
'max-age=60',
277276
'sameSite=strict',
277+
'max-age=60',
278278
'domain=example.com',
279279
'partitioned',
280280
'path=/nested',
@@ -297,8 +297,8 @@ describe("localePrefix: 'always', with `localeCookie`", () => {
297297
expect(cookieSpy).toHaveBeenCalledWith(
298298
[
299299
'NEXT_LOCALE=de',
300-
'max-age=60',
301300
'sameSite=strict',
301+
'max-age=60',
302302
'domain=example.com',
303303
'partitioned',
304304
'path=/nested',
@@ -345,12 +345,7 @@ describe("localePrefix: 'always', with `basePath`", () => {
345345
invokeRouter((router) => router.push('/about', {locale: 'de'}));
346346

347347
expect(cookieSpy).toHaveBeenCalledWith(
348-
[
349-
'NEXT_LOCALE=de',
350-
'max-age=18000',
351-
'sameSite=lax',
352-
'path=/base/path'
353-
].join(';') + ';'
348+
['NEXT_LOCALE=de', 'sameSite=lax', 'path=/base/path'].join(';') + ';'
354349
);
355350
cookieSpy.mockRestore();
356351
});

packages/next-intl/src/routing/config.tsx

+1-2
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ function receiveLocaleCookie(
157157
return (localeCookie ?? true)
158158
? {
159159
name: 'NEXT_LOCALE',
160-
maxAge: 5 * 60 * 60, // 5 hours
161160
sameSite: 'lax',
162161
...(typeof localeCookie === 'object' && localeCookie)
163162

@@ -173,7 +172,7 @@ export type LocaleCookieConfig = Omit<
173172
CookieAttributes,
174173
'name' | 'maxAge' | 'sameSite'
175174
> &
176-
Required<Pick<CookieAttributes, 'name' | 'maxAge' | 'sameSite'>>;
175+
Required<Pick<CookieAttributes, 'name' | 'sameSite'>>;
177176

178177
function receiveLocalePrefixConfig<
179178
AppLocales extends Locales,

0 commit comments

Comments
 (0)