@@ -26,6 +26,33 @@ const disableFaultyTransferEncodingHandling = (res: ComputeJsOutgoingMessage) =>
2626 }
2727}
2828
29+ const getHeaderValueArray = ( header : string ) : string [ ] => {
30+ return header
31+ . split ( ',' )
32+ . map ( ( value ) => value . trim ( ) )
33+ . filter ( Boolean )
34+ }
35+
36+ const omitHeaderValues = ( header : string , values : string [ ] ) : string => {
37+ const headerValues = getHeaderValueArray ( header )
38+ const filteredValues = headerValues . filter (
39+ ( value ) => ! values . some ( ( val ) => value . startsWith ( val ) ) ,
40+ )
41+ return filteredValues . join ( ', ' )
42+ }
43+
44+ /**
45+ * https://httpwg.org/specs/rfc9211.html
46+ *
47+ * We get HIT, MISS, STALE statuses from Next cache.
48+ * We will ignore other statuses and will not set Cache-Status header in those cases.
49+ */
50+ const NEXT_CACHE_TO_CACHE_STATUS : Record < string , string > = {
51+ HIT : `hit` ,
52+ MISS : `fwd=miss` ,
53+ STALE : `hit; fwd=stale` ,
54+ }
55+
2956type NextHandler = (
3057 req : IncomingMessage ,
3158 res : ServerResponse ,
@@ -39,6 +66,11 @@ export async function runNextHandler(
3966 context : Context ,
4067 nextHandler : NextHandler ,
4168) : Promise < Response > {
69+ console . log ( 'Handling request' , {
70+ url : request . url ,
71+ isDataRequest : request . headers . get ( 'x-nextjs-data' ) ,
72+ } )
73+
4274 const { req, res } = toReqRes ( request )
4375 // Work around a bug in http-proxy in next@<14.0.2
4476 Object . defineProperty ( req , 'connection' , {
@@ -71,5 +103,50 @@ export async function runNextHandler(
71103 } )
72104
73105 const response = await toComputeResponse ( res )
106+
107+ {
108+ // move cache-control to cdn-cache-control
109+ const cacheControl = response . headers . get ( 'cache-control' )
110+ if (
111+ cacheControl &&
112+ [ 'GET' , 'HEAD' ] . includes ( request . method ) &&
113+ ! response . headers . has ( 'cdn-cache-control' ) &&
114+ ! response . headers . has ( 'netlify-cdn-cache-control' )
115+ ) {
116+ // handle CDN Cache Control on ISR and App Router page responses
117+ const browserCacheControl = omitHeaderValues ( cacheControl , [
118+ 's-maxage' ,
119+ 'stale-while-revalidate' ,
120+ ] )
121+ const cdnCacheControl =
122+ // if we are serving already stale response, instruct edge to not attempt to cache that response
123+ response . headers . get ( 'x-nextjs-cache' ) === 'STALE'
124+ ? 'public, max-age=0, must-revalidate, durable'
125+ : [
126+ ...getHeaderValueArray ( cacheControl ) . map ( ( value ) =>
127+ value === 'stale-while-revalidate' ? 'stale-while-revalidate=31536000' : value ,
128+ ) ,
129+ 'durable' ,
130+ ] . join ( ', ' )
131+
132+ response . headers . set (
133+ 'cache-control' ,
134+ browserCacheControl || 'public, max-age=0, must-revalidate' ,
135+ )
136+ response . headers . set ( 'netlify-cdn-cache-control' , cdnCacheControl )
137+ }
138+ }
139+
140+ {
141+ // set Cache-Status header based on Next.js cache status
142+ const nextCache = response . headers . get ( 'x-nextjs-cache' )
143+ if ( nextCache ) {
144+ if ( nextCache in NEXT_CACHE_TO_CACHE_STATUS ) {
145+ response . headers . set ( 'cache-status' , NEXT_CACHE_TO_CACHE_STATUS [ nextCache ] )
146+ }
147+ response . headers . delete ( 'x-nextjs-cache' )
148+ }
149+ }
150+
74151 return response
75152}
0 commit comments