@@ -8,22 +8,48 @@ import type { TagsManifest } from './config.js'
8
8
import type { RequestContext } from './handlers/request-context.cjs'
9
9
import type { RuntimeTracer } from './handlers/tracer.cjs'
10
10
11
+ const ALL_VARIATIONS = Symbol . for ( 'ALL_VARIATIONS' )
11
12
interface NetlifyVaryValues {
12
- headers : string [ ]
13
- languages : string [ ]
14
- cookies : string [ ]
13
+ header : string [ ]
14
+ language : string [ ]
15
+ cookie : string [ ]
16
+ /**
17
+ * Query variation can be without argument in which case all query combinations would create a new cache key
18
+ * This is represented by a ALL_VARIATIONS in the array.
19
+ */
20
+ query : ( string | typeof ALL_VARIATIONS ) [ ]
21
+ country : string [ ]
15
22
}
16
23
17
- const generateNetlifyVaryValues = ( { headers, languages, cookies } : NetlifyVaryValues ) : string => {
24
+ const NetlifyVaryKeys = new Set ( [ 'header' , 'language' , 'cookie' , 'query' , 'country' ] )
25
+ const isNetlifyVaryKey = ( key : string ) : key is keyof NetlifyVaryValues => NetlifyVaryKeys . has ( key )
26
+
27
+ const generateNetlifyVaryValues = ( {
28
+ header,
29
+ language,
30
+ cookie,
31
+ query,
32
+ country,
33
+ } : NetlifyVaryValues ) : string => {
18
34
const values : string [ ] = [ ]
19
- if ( headers . length !== 0 ) {
20
- values . push ( `header=${ headers . join ( `|` ) } ` )
35
+ if ( query . length !== 0 ) {
36
+ if ( query . includes ( ALL_VARIATIONS ) ) {
37
+ values . push ( `query` )
38
+ } else {
39
+ values . push ( `query=${ query . join ( `|` ) } ` )
40
+ }
41
+ }
42
+ if ( header . length !== 0 ) {
43
+ values . push ( `header=${ header . join ( `|` ) } ` )
44
+ }
45
+ if ( language . length !== 0 ) {
46
+ values . push ( `language=${ language . join ( `|` ) } ` )
21
47
}
22
- if ( languages . length !== 0 ) {
23
- values . push ( `language =${ languages . join ( `|` ) } ` )
48
+ if ( cookie . length !== 0 ) {
49
+ values . push ( `cookie =${ cookie . join ( `|` ) } ` )
24
50
}
25
- if ( cookies . length !== 0 ) {
26
- values . push ( `cookie =${ cookies . join ( `|` ) } ` )
51
+ if ( country . length !== 0 ) {
52
+ values . push ( `country =${ country . join ( `|` ) } ` )
27
53
}
28
54
return values . join ( ',' )
29
55
}
@@ -56,22 +82,40 @@ export const setVaryHeaders = (
56
82
{ basePath, i18n } : Pick < NextConfigComplete , 'basePath' | 'i18n' > ,
57
83
) => {
58
84
const netlifyVaryValues : NetlifyVaryValues = {
59
- headers : [ 'x-nextjs-data' ] ,
60
- languages : [ ] ,
61
- cookies : [ '__prerender_bypass' , '__next_preview_data' ] ,
85
+ header : [ 'x-nextjs-data' ] ,
86
+ language : [ ] ,
87
+ cookie : [ '__prerender_bypass' , '__next_preview_data' ] ,
88
+ query : [ ] ,
89
+ country : [ ] ,
62
90
}
63
91
64
92
const vary = headers . get ( 'vary' )
65
93
if ( vary !== null ) {
66
- netlifyVaryValues . headers . push ( ...getHeaderValueArray ( vary ) )
94
+ netlifyVaryValues . header . push ( ...getHeaderValueArray ( vary ) )
67
95
}
68
96
69
97
const path = new URL ( request . url ) . pathname
70
98
const locales = i18n && i18n . localeDetection !== false ? i18n . locales : [ ]
71
99
72
100
if ( locales . length > 1 && ( path === '/' || path === basePath ) ) {
73
- netlifyVaryValues . languages . push ( ...locales )
74
- netlifyVaryValues . cookies . push ( `NEXT_LOCALE` )
101
+ netlifyVaryValues . language . push ( ...locales )
102
+ netlifyVaryValues . cookie . push ( `NEXT_LOCALE` )
103
+ }
104
+
105
+ const userNetlifyVary = headers . get ( 'netlify-vary' )
106
+ if ( userNetlifyVary ) {
107
+ // respect user's header and append them
108
+ const directives = getHeaderValueArray ( userNetlifyVary )
109
+ for ( const directive of directives ) {
110
+ const [ key , value ] = directive . split ( '=' )
111
+
112
+ if ( key === 'query' && ! value ) {
113
+ // query can have no "assignment" and then it should vary on all possible query combinations
114
+ netlifyVaryValues . query . push ( ALL_VARIATIONS )
115
+ } else if ( value && isNetlifyVaryKey ( key ) ) {
116
+ netlifyVaryValues [ key ] . push ( ...value . split ( '|' ) )
117
+ }
118
+ }
75
119
}
76
120
77
121
headers . set ( `netlify-vary` , generateNetlifyVaryValues ( netlifyVaryValues ) )
@@ -182,6 +226,7 @@ export const setCacheControlHeaders = (
182
226
if (
183
227
typeof requestContext . routeHandlerRevalidate !== 'undefined' &&
184
228
[ 'GET' , 'HEAD' ] . includes ( request . method ) &&
229
+ ! headers . has ( 'cdn-cache-control' ) &&
185
230
! headers . has ( 'netlify-cdn-cache-control' )
186
231
) {
187
232
// handle CDN Cache Control on Route Handler responses
0 commit comments