Skip to content

Commit dc54233

Browse files
author
Peter Bengtsson
authored
Simple 404 response on old _next/data/ requests (#37756)
1 parent ac7a172 commit dc54233

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* This middleware looks at the URL if it's something like:
3+
*
4+
* /_next/data/oOIffMZgfjR6sR9pa50O9/en/free-pro-team%40latest/pages.json?...
5+
*
6+
* And from that, it compares that oOIffMZgfjR6sR9pa50O9 with the content
7+
* of the .next/BUILD_ID file. If they don't match, then it's going to 404.
8+
* But instead of letting the nextApp.render404() handle it, we're going to
9+
* manually handle it here.
10+
* This makes sure the response is a short and fast plain text 404 response.
11+
* And we can force it to be served with a cache-control which allows
12+
* the CDN to cache it a bit.
13+
*
14+
* Note that when you start the local server with `npm run dev` and
15+
* do client-side navigation in the app, NextJS will send XHR requests for...
16+
*
17+
* /_next/data/development/en/free-pro-team%40latest/pages.json?...
18+
*
19+
* Relying on that test is easier than to try to parse the
20+
* value of `process.env.NODE_ENV`.
21+
*/
22+
23+
import fs from 'fs'
24+
25+
import { errorCacheControl } from '../../../middleware/cache-control.js'
26+
27+
export default function handleOldNextDataPaths(req, res, next) {
28+
if (req.path.startsWith('/_next/data/') && !req.path.startsWith('/_next/data/development/')) {
29+
const requestBuildId = req.path.split('/')[3]
30+
if (requestBuildId !== getCurrentBuildID()) {
31+
errorCacheControl(res)
32+
return res.status(404).type('text').send('build ID mismatch')
33+
}
34+
}
35+
return next()
36+
}
37+
38+
let _buildId
39+
function getCurrentBuildID() {
40+
// Simple memoization
41+
if (!_buildId) {
42+
_buildId = fs.readFileSync('.next/BUILD_ID', 'utf-8').trim()
43+
}
44+
return _buildId
45+
}

src/shielding/middleware/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import express from 'express'
22

33
import handleInvalidQuerystrings from './handle-invalid-query-strings.js'
44
import handleInvalidPaths from './handle-invalid-paths.js'
5+
import handleOldNextDataPaths from './handle-old-next-data-paths.js'
56
import rateLimit from './rate-limit.js'
67

78
const router = express.Router()
89

910
router.use(rateLimit)
1011
router.use(handleInvalidQuerystrings)
1112
router.use(handleInvalidPaths)
13+
router.use(handleOldNextDataPaths)
1214

1315
export default router

src/shielding/tests/shielding.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,12 @@ describe('404 pages and their content-type', () => {
9797
expect(res.headers['content-type']).toMatch('text/html')
9898
})
9999
})
100+
101+
describe('/_next/data/... paths', () => {
102+
test('invalid build ID', async () => {
103+
const res = await get('/_next/data/madeupbutnotvalid/en/free-pro-team%40latest/pages.json')
104+
expect(res.statusCode).toBe(404)
105+
expect(res.headers['content-type']).toMatch('text/plain')
106+
expect(res.headers['cache-control']).toMatch('public')
107+
})
108+
})

0 commit comments

Comments
 (0)