Skip to content

fix: fix cache analysis not rendering when edge cache miss occurs #92

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions app/utils/getCacheAnalysis.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,39 @@ describe('getCacheAnalysis', () => {

expect(() => getCacheAnalysis(headers, now)).toThrow('Could not determine who served the request')
})

it('handles cache miss without function headers (issue reproduction)', () => {
// This reproduces the exact headers from the issue where no cache analysis is rendered
const headers = {
'age': '0',
'cache-control': 'public,max-age=0,must-revalidate',
'cache-status': '"Netlify Edge"; fwd=miss',
'content-encoding': 'br',
'content-type': 'text/html; charset=UTF-8',
'date': 'Sat, 05 Jul 2025 00:18:32 GMT',
'debug-x-bb-deploy-id': '6866a5e3ddbff100081b7ff8',
'debug-x-bb-gen': '6866a5e3ddbff100081b7ff8:1751557850611',
'debug-x-bb-host-id': 'cdn-glo-aws-cmh-57, cdn-glo-aws-cmh-57',
'debug-x-nf-cache-info': 'hit=0,fresh=0,swr=0,cacheable=1,mem=0,rww=0,ort=1,owt=1',
'debug-x-nf-cache-result': 'miss',
'etag': '"92de5e34923e8d6e08269210e3bae88d-ssl-df"',
'vary': 'Accept-Encoding',
}
const now = Date.now()

// This should now work correctly instead of throwing an error
const result = getCacheAnalysis(headers, now)

expect(result).toHaveProperty('servedBy')
expect(result).toHaveProperty('cacheStatus')
expect(result).toHaveProperty('cacheControl')
expect(result.servedBy.source).toBe(ServedBySource.CDN)
expect(result.servedBy.cdnNodes).toBe('cdn-glo-aws-cmh-57')
expect(result.cacheStatus).toHaveLength(1)
expect(result.cacheStatus[0]?.cacheName).toBe('Netlify Edge')
expect(result.cacheStatus[0]?.parameters.hit).toBe(false)
expect(result.cacheStatus[0]?.parameters.fwd).toBe('miss')
})
})

describe('parseCacheStatus', () => {
Expand Down
6 changes: 4 additions & 2 deletions app/utils/getCacheAnalysis.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { getServedBy, type ServedBy } from './getServedBy'
import { parseCacheControl, type ParsedCacheControl } from './parseCacheControl'
import { getServedBy } from './getServedBy'
import type { ServedBy } from './getServedBy'
import { parseCacheControl } from './parseCacheControl'
import type { ParsedCacheControl } from './parseCacheControl'

const CACHE_NAMES_SORTED_BY_RFC_9211 = [
'Next.js',
Expand Down
25 changes: 24 additions & 1 deletion app/utils/getServedBy.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, it, expect } from 'vitest'

import { getServedBy, ServedBySource, type ParsedCacheStatusEntry } from './getServedBy'
import { getServedBy, ServedBySource } from './getServedBy'
import type { ParsedCacheStatusEntry } from './getServedBy'

describe('getServedBy', () => {
it('returns CDN when Netlify Edge cache has a hit', () => {
Expand Down Expand Up @@ -183,4 +184,26 @@ describe('getServedBy', () => {

expect(result.source).toBe(ServedBySource.DurableCache)
})

it('falls back to CDN when no cache hits and no function headers but CDN host is present', () => {
const headers = new Headers({
'Debug-X-BB-Host-Id': 'cdn-glo-aws-cmh-57, cdn-glo-aws-cmh-57',
})
const cacheStatus: ParsedCacheStatusEntry[] = [
{
cacheName: 'Netlify Edge',
parameters: {
hit: false,
fwd: 'miss',
stored: false,
collapsed: false,
},
},
]

const result = getServedBy(headers, cacheStatus)

expect(result.source).toBe(ServedBySource.CDN)
expect(result.cdnNodes).toBe('cdn-glo-aws-cmh-57')
})
})
5 changes: 5 additions & 0 deletions app/utils/getServedBy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ const getServedBySource = (
if (cacheHeaders.has('Debug-X-NF-Edge-Functions'))
return ServedBySource.EdgeFunction

// If no cache hits and no function headers, but CDN was involved (indicated by Debug-X-BB-Host-Id),
// then the CDN served the request (likely a cache miss that was forwarded to origin)
if (cacheHeaders.has('Debug-X-BB-Host-Id'))
return ServedBySource.CDN

throw new Error(
`Could not determine who served the request. Cache status: ${cacheStatus}`,
)
Expand Down
Loading