From 9e7b7f56aef31de10f12a726f5f3375566b32802 Mon Sep 17 00:00:00 2001 From: Muhammad Abdul Rehman Date: Sun, 22 Mar 2026 12:15:11 +0500 Subject: [PATCH 1/2] fix(@angular/ssr): apply forwarded prefix and vary header in accept-language redirects --- packages/angular/ssr/src/app-engine.ts | 16 +++++++++------- packages/angular/ssr/test/app-engine_spec.ts | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/angular/ssr/src/app-engine.ts b/packages/angular/ssr/src/app-engine.ts index de4eb1efa14f..023262406939 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'; @@ -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 () => { From 859406cf7f67cce28cf3115b97eda7f23bb5c0bc Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Mon, 23 Mar 2026 10:18:31 +0000 Subject: [PATCH 2/2] fixup! fix(@angular/ssr): apply forwarded prefix and vary header in accept-language redirects --- packages/angular/ssr/src/app-engine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular/ssr/src/app-engine.ts b/packages/angular/ssr/src/app-engine.ts index 023262406939..f7babe9beaf7 100644 --- a/packages/angular/ssr/src/app-engine.ts +++ b/packages/angular/ssr/src/app-engine.ts @@ -147,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;