diff --git a/packages/angular/ssr/src/app-engine.ts b/packages/angular/ssr/src/app-engine.ts index de4eb1efa14f..f7babe9beaf7 100644 --- a/packages/angular/ssr/src/app-engine.ts +++ b/packages/angular/ssr/src/app-engine.ts @@ -10,6 +10,7 @@ import type { AngularServerApp, getOrCreateAngularServerApp } from './app'; import { Hooks } from './hooks'; import { getPotentialLocaleIdFromUrl, getPreferredLocale } from './i18n'; import { EntryPointExports, getAngularAppEngineManifest } from './manifest'; +import { createRedirectResponse } from './utils/redirect'; import { joinUrlParts } from './utils/url'; import { cloneRequestAndPatchHeaders, validateRequest } from './utils/validation'; @@ -146,7 +147,7 @@ export class AngularAppEngine { if (this.supportedLocales.length > 1) { // Redirect to the preferred language if i18n is enabled. - return this.redirectBasedOnAcceptLanguage(request); + return this.redirectBasedOnAcceptLanguage(securedRequest); } return null; @@ -179,13 +180,14 @@ export class AngularAppEngine { if (preferredLocale) { const subPath = supportedLocales[preferredLocale]; if (subPath !== undefined) { - return new Response(null, { - status: 302, // Use a 302 redirect as language preference may change. - headers: { - 'Location': joinUrlParts(pathname, subPath), - 'Vary': 'Accept-Language', - }, - }); + const prefix = request.headers.get('X-Forwarded-Prefix') ?? ''; + + return createRedirectResponse( + joinUrlParts(prefix, pathname, subPath), + 302, + // Use a 302 redirect as language preference may change. + { 'Vary': 'Accept-Language' }, + ); } } diff --git a/packages/angular/ssr/test/app-engine_spec.ts b/packages/angular/ssr/test/app-engine_spec.ts index 7f8d24dce77a..14e664659748 100644 --- a/packages/angular/ssr/test/app-engine_spec.ts +++ b/packages/angular/ssr/test/app-engine_spec.ts @@ -160,7 +160,21 @@ describe('AngularAppEngine', () => { const response = await appEngine.handle(request); expect(response?.status).toBe(302); expect(response?.headers.get('Location')).toBe('/it'); - expect(response?.headers.get('Vary')).toBe('Accept-Language'); + expect(response?.headers.get('Vary')).toBe('X-Forwarded-Prefix, Accept-Language'); + }); + + it('should include forwarded prefix in locale redirect location when present', async () => { + const request = new Request('https://example.com', { + headers: { + 'Accept-Language': 'it', + 'X-Forwarded-Prefix': '/app', + }, + }); + + const response = await appEngine.handle(request); + expect(response?.status).toBe(302); + expect(response?.headers.get('Location')).toBe('/app/it'); + expect(response?.headers.get('Vary')).toBe('X-Forwarded-Prefix, Accept-Language'); }); it('should return null for requests to file-like resources in a locale', async () => {