@@ -125,32 +125,63 @@ export function parseResponseHeaders<TRequest, TResponse>(
125125 ? headers . get ( "cdn-cache-control" )
126126 : null ;
127127
128- const finalCacheControl = cdnCacheControlHeader || cacheControlHeader ;
129-
130128 if ( cdnCacheControlHeader ) {
131129 result . headersToRemove . push ( "cdn-cache-control" ) ;
132130 }
133131
134- if ( finalCacheControl ) {
135- const directives = parseCacheControl ( finalCacheControl ) ;
136- result . isPrivate = ! ! directives . private ;
137- result . noCache = ! ! directives [ "no-cache" ] ;
138- result . noStore = ! ! directives [ "no-store" ] ;
132+ // Parse cdn-cache-control first (if present)
133+ if ( cdnCacheControlHeader ) {
134+ const cdnDirectives = parseCacheControl ( cdnCacheControlHeader ) ;
135+ result . isPrivate = ! ! cdnDirectives . private ;
136+ result . noCache = ! ! cdnDirectives [ "no-cache" ] ;
137+ result . noStore = ! ! cdnDirectives [ "no-store" ] ;
138+
139+ // cdn-cache-control uses max-age
140+ if ( typeof cdnDirectives [ "max-age" ] === "number" ) {
141+ result . ttl = cdnDirectives [ "max-age" ] ;
142+ }
143+
144+ if ( typeof cdnDirectives [ "stale-while-revalidate" ] === "number" ) {
145+ result . staleWhileRevalidate = cdnDirectives [ "stale-while-revalidate" ] ;
146+ }
147+ }
139148
140- if ( typeof directives [ "max-age" ] === "number" ) {
141- result . ttl = directives [ "max-age" ] ;
149+ // Parse regular cache-control (fallback for properties not set by cdn-cache-control)
150+ if ( cacheControlHeader ) {
151+ const directives = parseCacheControl ( cacheControlHeader ) ;
152+
153+ // Only use cache-control for properties not already set by cdn-cache-control
154+ if ( ! cdnCacheControlHeader ) {
155+ result . isPrivate = ! ! directives . private ;
156+ result . noCache = ! ! directives [ "no-cache" ] ;
157+ result . noStore = ! ! directives [ "no-store" ] ;
142158 }
143159
144- if ( typeof directives [ "stale-while-revalidate" ] === "number" ) {
160+ // cache-control only uses s-maxage for TTL (ignore max-age)
161+ if ( ! result . ttl && typeof directives [ "s-maxage" ] === "number" ) {
162+ result . ttl = directives [ "s-maxage" ] ;
163+ }
164+
165+ if ( ! result . staleWhileRevalidate && typeof directives [ "stale-while-revalidate" ] === "number" ) {
145166 result . staleWhileRevalidate = directives [ "stale-while-revalidate" ] ;
146167 }
168+
169+ // Filter out used directives from cache-control
170+ const filteredCacheControl = filterCacheControlDirectives ( cacheControlHeader ) ;
171+ if ( filteredCacheControl !== cacheControlHeader && filteredCacheControl . trim ( ) ) {
172+ // Only modify cache-control if we have remaining directives
173+ result . filteredCacheControl = filteredCacheControl ;
174+ } else if ( filteredCacheControl !== cacheControlHeader ) {
175+ // If we filtered everything out, remove the header entirely
176+ result . headersToRemove . push ( "cache-control" ) ;
177+ }
147178 }
148179
149180 if ( features . cacheTags !== false ) {
150181 const cacheTag = headers . get ( "cache-tag" ) ;
151182 if ( cacheTag ) {
152183 result . tags = parseCacheTags ( cacheTag ) ;
153- result . headersToRemove . push ( "cache-tag" ) ;
184+ // Cache tags should be preserved for clients, not removed
154185 }
155186 }
156187
@@ -188,7 +219,7 @@ export function parseResponseHeaders<TRequest, TResponse>(
188219 }
189220
190221 // Cache only when explicitly allowed by headers (no implicit caching)
191- const hasExplicitCacheHeaders = ! ! finalCacheControl ||
222+ const hasExplicitCacheHeaders = ! ! cacheControlHeader || ! ! cdnCacheControlHeader ||
192223 ! ! headers . get ( "cache-tag" ) ||
193224 ! ! headers . get ( "expires" ) ;
194225 result . shouldCache = hasExplicitCacheHeaders &&
@@ -317,11 +348,44 @@ function getCookieValue(
317348 return null ;
318349}
319350
351+ /**
352+ * Remove cache-control directives that were processed by cache-handlers
353+ */
354+ export function filterCacheControlDirectives (
355+ cacheControlValue : string ,
356+ ) : string {
357+ const directives = parseCacheControl ( cacheControlValue ) ;
358+
359+ // Remove directives that cache-handlers processes
360+ const usedDirectives = [
361+ "s-maxage" , // CDN-specific, we've processed it
362+ "stale-while-revalidate" // Our SWR implementation
363+ ] ;
364+
365+ // Remove used directives
366+ for ( const directive of usedDirectives ) {
367+ delete directives [ directive ] ;
368+ }
369+
370+ // Rebuild cache-control header from remaining directives
371+ const remaining = Object . entries ( directives )
372+ . map ( ( [ key , value ] ) => {
373+ if ( value === true ) {
374+ return key ;
375+ }
376+ return `${ key } =${ value } ` ;
377+ } )
378+ . filter ( Boolean ) ;
379+
380+ return remaining . join ( ", " ) ;
381+ }
382+
320383export function removeHeaders (
321384 response : Response ,
322385 headersToRemove : string [ ] ,
386+ filteredCacheControl ?: string ,
323387) : Response {
324- if ( headersToRemove . length === 0 ) {
388+ if ( headersToRemove . length === 0 && ! filteredCacheControl ) {
325389 return response ;
326390 }
327391
@@ -330,6 +394,15 @@ export function removeHeaders(
330394 newHeaders . delete ( headerName ) ;
331395 }
332396
397+ // Apply filtered cache-control if provided
398+ if ( filteredCacheControl !== undefined ) {
399+ if ( filteredCacheControl . trim ( ) ) {
400+ newHeaders . set ( "cache-control" , filteredCacheControl ) ;
401+ } else {
402+ newHeaders . delete ( "cache-control" ) ;
403+ }
404+ }
405+
333406 return new Response ( response . body , {
334407 status : response . status ,
335408 statusText : response . statusText ,
0 commit comments