File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ import statsd from '#src/observability/lib/statsd.js'
2+ import { errorCacheControl } from '#src/frame/middleware/cache-control.js'
3+
4+ const STATSD_KEY = 'middleware.handle_invalid_headers'
5+
6+ const INVALID_HEADER_KEYS = [
7+ // Next.js will pick this up and override the status code.
8+ // We don't want that to happen because `x-invoke-status: 203` can
9+ // trigger the CDN to cache it.
10+ // It can also trigger a 500 error because the header is not used
11+ // correctly.
12+ 'x-invoke-status' ,
13+ ]
14+
15+ export default function handleInvalidNextPaths ( req , res , next ) {
16+ const header = INVALID_HEADER_KEYS . find ( ( key ) => req . headers [ key ] )
17+ if ( header ) {
18+ // This way you can't hammer the backend with invalid requests.
19+ // Since the CDN will cache based on the status code not being one
20+ // of success, we don't have to worry about this being cached when
21+ // the URL is the same but the headers *not invalid*.
22+ errorCacheControl ( res )
23+
24+ const tags = [ `ip:${ req . ip } ` , `path:${ req . path } ` , `header:${ header } ` ]
25+ statsd . increment ( STATSD_KEY , 1 , tags )
26+
27+ return res . status ( 400 ) . type ( 'text' ) . send ( 'Invalid request headers' )
28+ }
29+
30+ return next ( )
31+ }
Original file line number Diff line number Diff line change @@ -5,6 +5,7 @@ import handleInvalidPaths from './handle-invalid-paths.js'
55import handleOldNextDataPaths from './handle-old-next-data-paths.js'
66import handleInvalidQuerystringValues from './handle-invalid-query-string-values.js'
77import handleInvalidNextPaths from './handle-invalid-nextjs-paths.js'
8+ import handleInvalidHeaders from './handle-invalid-headers.js'
89import rateLimit from './rate-limit.js'
910
1011const router = express . Router ( )
@@ -15,5 +16,6 @@ router.use(handleInvalidPaths)
1516router . use ( handleOldNextDataPaths )
1617router . use ( handleInvalidQuerystringValues )
1718router . use ( handleInvalidNextPaths )
19+ router . use ( handleInvalidHeaders )
1820
1921export default router
Original file line number Diff line number Diff line change 1+ import { get } from '#src/tests/helpers/e2etest.js'
2+
3+ describe ( 'invalid headers' , ( ) => {
4+ test ( '400 if containing x-invoke-status (instead of redirecting)' , async ( ) => {
5+ const res = await get ( '/' , { headers : { 'x-invoke-status' : '203' } } )
6+ expect ( res . statusCode ) . toBe ( 400 )
7+ expect ( res . headers [ 'cache-control' ] ) . toMatch ( 'public' )
8+ expect ( res . headers [ 'cache-control' ] ) . toMatch ( / m a x - a g e = [ 1 - 9 ] / )
9+ } )
10+ test ( '400 if containing x-invoke-status (instead of 200)' , async ( ) => {
11+ const res = await get ( '/en' , { headers : { 'x-invoke-status' : '203' } } )
12+ expect ( res . statusCode ) . toBe ( 400 )
13+ expect ( res . headers [ 'cache-control' ] ) . toMatch ( 'public' )
14+ expect ( res . headers [ 'cache-control' ] ) . toMatch ( / m a x - a g e = [ 1 - 9 ] / )
15+ } )
16+ } )
You can’t perform that action at this time.
0 commit comments