@@ -38,7 +38,10 @@ import { addHeadersToResponse } from "./headers/addHeaders";
38
38
import { isValidPreviewRequest } from "./lib/isValidPreviewRequest" ;
39
39
import { getUnauthenticatedResponse } from "./auth/authenticator" ;
40
40
import { buildS3RetryStrategy } from "./s3/s3RetryStrategy" ;
41
- import { isLocaleIndexUri } from "./routing/locale-utils" ;
41
+ import {
42
+ isLocalePrefixedUri ,
43
+ removeLocalePrefixFromUri
44
+ } from "./routing/locale-utils" ;
42
45
43
46
const basePath = RoutesManifestJson . basePath ;
44
47
@@ -67,13 +70,17 @@ const addS3HostHeader = (
67
70
68
71
const isDataRequest = ( uri : string ) : boolean => uri . startsWith ( "/_next/data" ) ;
69
72
70
- const normaliseUri = ( uri : string ) : string => {
73
+ const normaliseUri = ( uri : string , routesManifest : RoutesManifest ) : string => {
71
74
if ( basePath ) {
72
75
if ( uri . startsWith ( basePath ) ) {
73
76
uri = uri . slice ( basePath . length ) ;
74
77
} else {
75
78
// basePath set but URI does not start with basePath, return 404
76
- return "/404" ;
79
+ if ( routesManifest . i18n ?. defaultLocale ) {
80
+ return `/${ routesManifest . i18n ?. defaultLocale } /404` ;
81
+ } else {
82
+ return "/404" ;
83
+ }
77
84
}
78
85
}
79
86
@@ -217,7 +224,8 @@ export const handler = async (
217
224
response = await handleOriginResponse ( {
218
225
event,
219
226
manifest,
220
- prerenderManifest
227
+ prerenderManifest,
228
+ routesManifest
221
229
} ) ;
222
230
} else {
223
231
response = await handleOriginRequest ( {
@@ -277,7 +285,7 @@ const handleOriginRequest = async ({
277
285
}
278
286
279
287
const basePath = routesManifest . basePath ;
280
- let uri = normaliseUri ( request . uri ) ;
288
+ let uri = normaliseUri ( request . uri , routesManifest ) ;
281
289
const decodedUri = decodeURI ( uri ) ;
282
290
const { pages, publicFiles } = manifest ;
283
291
@@ -294,7 +302,11 @@ const handleOriginRequest = async ({
294
302
if ( newUri . endsWith ( "/" ) ) {
295
303
newUri = newUri . slice ( 0 , - 1 ) ;
296
304
}
297
- } else if ( request . uri !== "/" && request . uri !== "" && uri !== "/404" ) {
305
+ } else if (
306
+ request . uri !== "/" &&
307
+ request . uri !== "" &&
308
+ ! uri . endsWith ( "/404" )
309
+ ) {
298
310
// HTML/SSR pages get redirected based on trailingSlash in next.config.js
299
311
// We do not redirect:
300
312
// 1. Unnormalised URI is "/" or "" as this could cause a redirect loop due to browsers appending trailing slash
@@ -324,6 +336,34 @@ const handleOriginRequest = async ({
324
336
) ;
325
337
}
326
338
339
+ // Handle root language redirect
340
+ const languageHeader = request . headers [ "accept-language" ] ;
341
+ const languageRedirectUri = getLanguageRedirect (
342
+ languageHeader ? languageHeader [ 0 ] . value : undefined ,
343
+ uri ,
344
+ routesManifest ,
345
+ manifest
346
+ ) ;
347
+
348
+ if ( languageRedirectUri ) {
349
+ return createRedirectResponse (
350
+ languageRedirectUri ,
351
+ request . querystring ,
352
+ 307
353
+ ) ;
354
+ }
355
+
356
+ // Always add default locale prefix to URIs without it that are not public files or data requests
357
+ const defaultLocale = routesManifest . i18n ?. defaultLocale ;
358
+ if (
359
+ defaultLocale &&
360
+ ! isLocalePrefixedUri ( uri , routesManifest ) &&
361
+ ! isPublicFile &&
362
+ ! isDataReq
363
+ ) {
364
+ uri = uri === "/" ? `/${ defaultLocale } ` : `/${ defaultLocale } ${ uri } ` ;
365
+ }
366
+
327
367
// Check for non-dynamic pages before rewriting
328
368
const isNonDynamicRoute =
329
369
pages . html . nonDynamic [ uri ] || pages . ssr . nonDynamic [ uri ] || isPublicFile ;
@@ -369,25 +409,17 @@ const handleOriginRequest = async ({
369
409
return await responsePromise ;
370
410
}
371
411
372
- uri = normaliseUri ( request . uri ) ;
373
- }
374
- }
412
+ uri = normaliseUri ( request . uri , routesManifest ) ;
375
413
376
- // Handle root language rewrite
377
- const languageHeader = request . headers [ "accept-language" ] ;
378
- const languageRedirectUri = getLanguageRedirect (
379
- languageHeader ? languageHeader [ 0 ] . value : undefined ,
380
- uri ,
381
- routesManifest ,
382
- manifest
383
- ) ;
384
-
385
- if ( languageRedirectUri ) {
386
- return createRedirectResponse (
387
- languageRedirectUri ,
388
- request . querystring ,
389
- 307
390
- ) ;
414
+ if (
415
+ defaultLocale &&
416
+ ! isLocalePrefixedUri ( uri , routesManifest ) &&
417
+ ! isPublicFile &&
418
+ ! isDataReq
419
+ ) {
420
+ uri = uri === "/" ? `/${ defaultLocale } ` : `/${ defaultLocale } ${ uri } ` ;
421
+ }
422
+ }
391
423
}
392
424
393
425
const isStaticPage = pages . html . nonDynamic [ uri ] ; // plain page without any props
@@ -396,7 +428,7 @@ const handleOriginRequest = async ({
396
428
const s3Origin = origin . s3 as CloudFrontS3Origin ;
397
429
const isHTMLPage = isStaticPage || isPrerenderedPage ;
398
430
const normalisedS3DomainName = normaliseS3OriginDomain ( s3Origin ) ;
399
- const hasFallback = hasFallbackForUri ( uri , prerenderManifest , manifest ) ;
431
+ const hasFallback = hasFallbackForUri ( uri , manifest , routesManifest ) ;
400
432
const { now, log } = perfLogger ( manifest . logLambdaExecutionTimes ) ;
401
433
const isPreviewRequest = isValidPreviewRequest (
402
434
request . headers . cookie ,
@@ -420,12 +452,7 @@ const handleOriginRequest = async ({
420
452
}
421
453
} else if ( isHTMLPage || hasFallback ) {
422
454
s3Origin . path = `${ basePath } /static-pages/${ manifest . buildId } ` ;
423
- let pageName ;
424
- if ( isLocaleIndexUri ( uri , routesManifest ) ) {
425
- pageName = `${ uri } /index` ;
426
- } else {
427
- pageName = uri === "/" ? "/index" : uri ;
428
- }
455
+ const pageName = uri === "/" ? "/index" : uri ;
429
456
request . uri = `${ pageName } .html` ;
430
457
} else if ( isDataReq ) {
431
458
// We need to check whether data request is unmatched i.e routed to 404.html or _error.js
@@ -441,8 +468,8 @@ const handleOriginRequest = async ({
441
468
( ! pages . ssg . nonDynamic [ normalisedDataRequestUri ] &&
442
469
! hasFallbackForUri (
443
470
normalisedDataRequestUri ,
444
- prerenderManifest ,
445
- manifest
471
+ manifest ,
472
+ routesManifest
446
473
) )
447
474
) {
448
475
// Break to continue to SSR render in two cases:
@@ -526,11 +553,13 @@ const handleOriginRequest = async ({
526
553
const handleOriginResponse = async ( {
527
554
event,
528
555
manifest,
529
- prerenderManifest
556
+ prerenderManifest,
557
+ routesManifest
530
558
} : {
531
559
event : OriginResponseEvent ;
532
560
manifest : OriginRequestDefaultHandlerManifest ;
533
561
prerenderManifest : PrerenderManifestType ;
562
+ routesManifest : RoutesManifest ;
534
563
} ) => {
535
564
const response = event . Records [ 0 ] . cf . response ;
536
565
const request = event . Records [ 0 ] . cf . request ;
@@ -549,7 +578,7 @@ const handleOriginResponse = async ({
549
578
return response ;
550
579
}
551
580
552
- const uri = normaliseUri ( request . uri ) ;
581
+ const uri = normaliseUri ( request . uri , routesManifest ) ;
553
582
const { domainName, region } = request . origin ! . s3 ! ;
554
583
const bucketName = domainName . replace ( `.s3.${ region } .amazonaws.com` , "" ) ;
555
584
@@ -615,7 +644,7 @@ const handleOriginResponse = async ({
615
644
res . end ( JSON . stringify ( renderOpts . pageData ) ) ;
616
645
return await responsePromise ;
617
646
} else {
618
- const hasFallback = hasFallbackForUri ( uri , prerenderManifest , manifest ) ;
647
+ const hasFallback = hasFallbackForUri ( uri , manifest , routesManifest ) ;
619
648
if ( ! hasFallback ) return response ;
620
649
621
650
// If route has fallback, return that page from S3, otherwise return 404 page
@@ -675,8 +704,8 @@ const isOriginResponse = (
675
704
676
705
const hasFallbackForUri = (
677
706
uri : string ,
678
- prerenderManifest : PrerenderManifestType ,
679
- manifest : OriginRequestDefaultHandlerManifest
707
+ manifest : OriginRequestDefaultHandlerManifest ,
708
+ routesManifest : RoutesManifest
680
709
) => {
681
710
const {
682
711
pages : { ssr, html, ssg }
@@ -712,7 +741,11 @@ const hasFallbackForUri = (
712
741
const matchesFallbackRoute = Object . keys ( ssg . dynamic ) . find (
713
742
( dynamicSsgRoute ) => {
714
743
const fileMatchesPrerenderRoute =
715
- dynamicRoute . file === `pages${ dynamicSsgRoute } .js` ;
744
+ dynamicRoute . file ===
745
+ `pages${ removeLocalePrefixFromUri (
746
+ dynamicSsgRoute ,
747
+ routesManifest
748
+ ) } .js`;
716
749
717
750
if ( fileMatchesPrerenderRoute ) {
718
751
foundFallback = ssg . dynamic [ dynamicSsgRoute ] ;
0 commit comments