Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion src/run/handlers/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default async (request: Request, context: FutureContext) => {

await adjustDateHeader({ headers: response.headers, request, span, tracer, requestContext })

setCacheControlHeaders(response.headers, request, requestContext)
setCacheControlHeaders(response.headers, response.status, request, requestContext)
setCacheTagsHeaders(response.headers, requestContext)
setVaryHeaders(response.headers, request, nextConfig)
setCacheStatusHeader(response.headers)
Expand Down
60 changes: 42 additions & 18 deletions src/run/headers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ describe('headers', () => {

describe('setCacheControlHeaders', () => {
const defaultUrl = 'https://example.com'
const defaultStatus = 200

describe('route handler responses with a specified `revalidate` value', () => {
test('should not set any headers if "cdn-cache-control" is present', () => {
Expand All @@ -204,7 +205,7 @@ describe('headers', () => {
vi.spyOn(headers, 'set')

const ctx: RequestContext = { ...createRequestContext(), routeHandlerRevalidate: false }
setCacheControlHeaders(headers, request, ctx)
setCacheControlHeaders(headers, defaultStatus, request, ctx)

expect(headers.set).toHaveBeenCalledTimes(0)
})
Expand All @@ -218,7 +219,7 @@ describe('headers', () => {
vi.spyOn(headers, 'set')

const ctx: RequestContext = { ...createRequestContext(), routeHandlerRevalidate: false }
setCacheControlHeaders(headers, request, ctx)
setCacheControlHeaders(headers, defaultStatus, request, ctx)

expect(headers.set).toHaveBeenCalledTimes(0)
})
Expand All @@ -232,7 +233,7 @@ describe('headers', () => {
vi.spyOn(headers, 'set')

const ctx: RequestContext = { ...createRequestContext(), routeHandlerRevalidate: false }
setCacheControlHeaders(headers, request, ctx)
setCacheControlHeaders(headers, defaultStatus, request, ctx)

expect(headers.set).toHaveBeenCalledTimes(1)
expect(headers.set).toHaveBeenNthCalledWith(
Expand All @@ -251,7 +252,7 @@ describe('headers', () => {
vi.spyOn(headers, 'set')

const ctx: RequestContext = { ...createRequestContext(), routeHandlerRevalidate: false }
setCacheControlHeaders(headers, request, ctx)
setCacheControlHeaders(headers, defaultStatus, request, ctx)

expect(headers.set).toHaveBeenCalledTimes(1)
expect(headers.set).toHaveBeenNthCalledWith(
Expand All @@ -267,7 +268,7 @@ describe('headers', () => {
vi.spyOn(headers, 'set')

const ctx: RequestContext = { ...createRequestContext(), routeHandlerRevalidate: false }
setCacheControlHeaders(headers, request, ctx)
setCacheControlHeaders(headers, defaultStatus, request, ctx)

expect(headers.set).toHaveBeenCalledTimes(1)
expect(headers.set).toHaveBeenNthCalledWith(
Expand All @@ -283,7 +284,7 @@ describe('headers', () => {
vi.spyOn(headers, 'set')

const ctx: RequestContext = { ...createRequestContext(), routeHandlerRevalidate: 7200 }
setCacheControlHeaders(headers, request, ctx)
setCacheControlHeaders(headers, defaultStatus, request, ctx)

expect(headers.set).toHaveBeenCalledTimes(1)
expect(headers.set).toHaveBeenNthCalledWith(
Expand All @@ -299,7 +300,7 @@ describe('headers', () => {
vi.spyOn(headers, 'set')

const ctx: RequestContext = { ...createRequestContext(), routeHandlerRevalidate: 7200 }
setCacheControlHeaders(headers, request, ctx)
setCacheControlHeaders(headers, defaultStatus, request, ctx)

expect(headers.set).toHaveBeenCalledTimes(1)
expect(headers.set).toHaveBeenNthCalledWith(
Expand All @@ -315,7 +316,7 @@ describe('headers', () => {
vi.spyOn(headers, 'set')

const ctx: RequestContext = { ...createRequestContext(), routeHandlerRevalidate: false }
setCacheControlHeaders(headers, request, ctx)
setCacheControlHeaders(headers, defaultStatus, request, ctx)

expect(headers.set).toHaveBeenCalledTimes(0)
})
Expand All @@ -326,7 +327,7 @@ describe('headers', () => {
const request = new Request(defaultUrl)
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenCalledTimes(0)
})
Expand All @@ -339,7 +340,30 @@ describe('headers', () => {
const requestContext = createRequestContext()
requestContext.usedFsRead = true

setCacheControlHeaders(headers, request, requestContext, true)
setCacheControlHeaders(headers, defaultStatus, request, requestContext)

expect(headers.set).toHaveBeenNthCalledWith(
1,
'cache-control',
'public, max-age=0, must-revalidate',
)
expect(headers.set).toHaveBeenNthCalledWith(
2,
'netlify-cdn-cache-control',
'max-age=31536000, durable',
)
})

test('should set permanent, durable "netlify-cdn-cache-control" if 404 response for URl ending in .php', () => {
const headers = new Headers()
const request = new Request('https://example.com/admin.php')
const status = 404
vi.spyOn(headers, 'set')

const requestContext = createRequestContext()
requestContext.usedFsRead = true

setCacheControlHeaders(headers, status, request, requestContext)

expect(headers.set).toHaveBeenNthCalledWith(
1,
Expand All @@ -362,7 +386,7 @@ describe('headers', () => {
const request = new Request(defaultUrl)
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenCalledTimes(0)
})
Expand All @@ -376,7 +400,7 @@ describe('headers', () => {
const request = new Request(defaultUrl)
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenCalledTimes(0)
})
Expand All @@ -389,7 +413,7 @@ describe('headers', () => {
const request = new Request(defaultUrl)
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenNthCalledWith(
1,
Expand All @@ -411,7 +435,7 @@ describe('headers', () => {
const request = new Request(defaultUrl, { method: 'HEAD' })
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenNthCalledWith(
1,
Expand All @@ -433,7 +457,7 @@ describe('headers', () => {
const request = new Request(defaultUrl, { method: 'POST' })
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenCalledTimes(0)
})
Expand All @@ -446,7 +470,7 @@ describe('headers', () => {
const request = new Request(defaultUrl)
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenNthCalledWith(1, 'cache-control', 'public')
expect(headers.set).toHaveBeenNthCalledWith(
Expand All @@ -464,7 +488,7 @@ describe('headers', () => {
const request = new Request(defaultUrl)
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenNthCalledWith(1, 'cache-control', 'max-age=604800')
expect(headers.set).toHaveBeenNthCalledWith(
Expand All @@ -482,7 +506,7 @@ describe('headers', () => {
const request = new Request(defaultUrl)
vi.spyOn(headers, 'set')

setCacheControlHeaders(headers, request, createRequestContext())
setCacheControlHeaders(headers, defaultStatus, request, createRequestContext())

expect(headers.set).toHaveBeenNthCalledWith(
1,
Expand Down
7 changes: 7 additions & 0 deletions src/run/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ export const adjustDateHeader = async ({
*/
export const setCacheControlHeaders = (
headers: Headers,
status: number,
request: Request,
requestContext: RequestContext,
) => {
Expand All @@ -231,6 +232,12 @@ export const setCacheControlHeaders = (
return
}

if (status === 404 && request.url.endsWith('.php')) {
// temporary CDN Cache Control handling for bot probes
headers.set('cache-control', 'public, max-age=0, must-revalidate')
headers.set('netlify-cdn-cache-control', `max-age=31536000, durable`)
}

const cacheControl = headers.get('cache-control')
if (
cacheControl !== null &&
Expand Down
7 changes: 7 additions & 0 deletions tests/integration/simple-app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ test<FixtureTestContext>('stale-while-revalidate headers should be normalized to
)
})

test<FixtureTestContext>('404 responses for PHP pages should be cached indefinitely', async (ctx) => {
await createFixture('simple', ctx)
await runPlugin(ctx)
const index = await invokeFunction(ctx, { url: '/admin.php' })
expect(index.headers?.['netlify-cdn-cache-control']).toContain('max-age=31536000, durable')
})

test<FixtureTestContext>('handlers receive correct site domain', async (ctx) => {
await createFixture('simple', ctx)
await runPlugin(ctx)
Expand Down
Loading