Skip to content

Commit 270487d

Browse files
timneutkenshuozhiijjk
authored
Make sure 404 pages do not get cached by a CDN when using next start (#24983)
Co-authored-by: Jiachi Liu <[email protected]> Co-authored-by: JJ Kasper <[email protected]>
1 parent 138b9dd commit 270487d

File tree

4 files changed

+79
-11
lines changed

4 files changed

+79
-11
lines changed

packages/next/build/webpack/loaders/next-serverless-loader/page-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ export function getPageHandler(ctx: ServerlessHandlerCtx) {
344344
poweredByHeader,
345345
},
346346
{
347-
private: isPreviewMode,
347+
private: isPreviewMode || page === '/404',
348348
stateful: !!getServerSideProps,
349349
revalidate: renderOpts.revalidate,
350350
}
@@ -385,7 +385,7 @@ export function getPageHandler(ctx: ServerlessHandlerCtx) {
385385
poweredByHeader,
386386
},
387387
{
388-
private: isPreviewMode,
388+
private: isPreviewMode || renderOpts.is404Page,
389389
stateful: !!getServerSideProps,
390390
revalidate: renderOpts.revalidate,
391391
}

packages/next/server/next-server.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,7 +1614,8 @@ export default class Server {
16141614

16151615
const revalidateOptions = !this.renderOpts.dev
16161616
? {
1617-
private: isPreviewMode,
1617+
// When the page is 404 cache-control should not be added
1618+
private: isPreviewMode || is404Page,
16181619
stateful: false, // GSP response
16191620
revalidate:
16201621
cachedData.curRevalidate !== undefined
@@ -1842,7 +1843,7 @@ export default class Server {
18421843
const revalidateOptions =
18431844
!this.renderOpts.dev || (hasServerProps && !isDataReq)
18441845
? {
1845-
private: isPreviewMode,
1846+
private: isPreviewMode || is404Page,
18461847
stateful: !isSSG,
18471848
revalidate: sprRevalidate,
18481849
}

test/integration/404-page/test/index.test.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ let nextConfigContent
2525
let appPort
2626
let app
2727

28+
const getCacheHeader = async (port, pathname) =>
29+
(await fetchViaHTTP(port, pathname)).headers.get('Cache-Control')
30+
2831
const runTests = (mode = 'server') => {
2932
it('should use pages/404', async () => {
3033
const html = await renderViaHTTP(appPort, '/abc')
@@ -108,6 +111,72 @@ describe('404 Page Support', () => {
108111
runTests('serverless')
109112
})
110113

114+
it('should not cache for custom 404 page with gssp and revalidate disabled', async () => {
115+
await fs.move(pages404, `${pages404}.bak`)
116+
await fs.writeFile(
117+
pages404,
118+
`
119+
const page = () => 'custom 404 page'
120+
export async function getStaticProps() { return { props: {} } }
121+
export default page
122+
`
123+
)
124+
await nextBuild(appDir)
125+
appPort = await findPort()
126+
app = await nextStart(appDir, appPort)
127+
const cache404 = await getCacheHeader(appPort, '/404')
128+
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
129+
await fs.remove(pages404)
130+
await fs.move(`${pages404}.bak`, pages404)
131+
await killApp(app)
132+
133+
expect(cache404).toBe(
134+
'private, no-cache, no-store, max-age=0, must-revalidate'
135+
)
136+
expect(cacheNext).toBe(
137+
'private, no-cache, no-store, max-age=0, must-revalidate'
138+
)
139+
})
140+
141+
it('should not cache for custom 404 page with gssp and revalidate enabled', async () => {
142+
await fs.move(pages404, `${pages404}.bak`)
143+
await fs.writeFile(
144+
pages404,
145+
`
146+
const page = () => 'custom 404 page'
147+
export async function getStaticProps() { return { props: {}, revalidate: 1 } }
148+
export default page
149+
`
150+
)
151+
await nextBuild(appDir)
152+
appPort = await findPort()
153+
app = await nextStart(appDir, appPort)
154+
const cache404 = await getCacheHeader(appPort, '/404')
155+
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
156+
await fs.remove(pages404)
157+
await fs.move(`${pages404}.bak`, pages404)
158+
await killApp(app)
159+
160+
expect(cache404).toBe(
161+
'private, no-cache, no-store, max-age=0, must-revalidate'
162+
)
163+
expect(cacheNext).toBe(
164+
'private, no-cache, no-store, max-age=0, must-revalidate'
165+
)
166+
})
167+
168+
it('should not cache for custom 404 page without gssp', async () => {
169+
await nextBuild(appDir)
170+
appPort = await findPort()
171+
app = await nextStart(appDir, appPort)
172+
const cache404 = await getCacheHeader(appPort, '/404')
173+
const cacheNext = await getCacheHeader(appPort, '/_next/abc')
174+
await killApp(app)
175+
176+
expect(cache404).toBe(null)
177+
expect(cacheNext).toBe('no-cache, no-store, max-age=0, must-revalidate')
178+
})
179+
111180
it('falls back to _error correctly without pages/404', async () => {
112181
await fs.move(pages404, `${pages404}.bak`)
113182
appPort = await findPort()

test/integration/not-found-revalidate/test/index.test.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,17 @@ const runTests = () => {
2222
let res = await fetchViaHTTP(appPort, '/fallback-blocking/hello')
2323
let $ = cheerio.load(await res.text())
2424

25-
expect(res.headers.get('cache-control')).toBe(
26-
's-maxage=1, stale-while-revalidate'
27-
)
25+
const privateCache =
26+
'private, no-cache, no-store, max-age=0, must-revalidate'
27+
expect(res.headers.get('cache-control')).toBe(privateCache)
2828
expect(res.status).toBe(404)
2929
expect(JSON.parse($('#props').text()).notFound).toBe(true)
3030

3131
await waitFor(1000)
3232
res = await fetchViaHTTP(appPort, '/fallback-blocking/hello')
3333
$ = cheerio.load(await res.text())
3434

35-
expect(res.headers.get('cache-control')).toBe(
36-
's-maxage=1, stale-while-revalidate'
37-
)
35+
expect(res.headers.get('cache-control')).toBe(privateCache)
3836
expect(res.status).toBe(404)
3937
expect(JSON.parse($('#props').text()).notFound).toBe(true)
4038

@@ -89,7 +87,7 @@ const runTests = () => {
8987
let $ = cheerio.load(await res.text())
9088

9189
expect(res.headers.get('cache-control')).toBe(
92-
's-maxage=1, stale-while-revalidate'
90+
'private, no-cache, no-store, max-age=0, must-revalidate'
9391
)
9492
expect(res.status).toBe(404)
9593
expect(JSON.parse($('#props').text()).notFound).toBe(true)

0 commit comments

Comments
 (0)