{"record":{"id":"10dade87c43d931a","repo":"angular/angular-cli","slug":"header-x-forwarded-prefix-is-invalid-it-must-st","errorCode":null,"errorMessage":"Header \"x-forwarded-prefix\" is invalid. It must start with a \"/\" and contain only alphanumeric characters, hyphens, and underscores, separated by single slashes.","messagePattern":"Header \"x-forwarded-prefix\" is invalid\\. It must start with a \"/\" and contain only alphanumeric characters, hyphens, and underscores, separated by single slashes\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular/ssr/src/utils/validation.ts","lineNumber":225,"sourceCode":"    }\n    if (forwardedParams.proto && !VALID_PROTO_REGEX.test(forwardedParams.proto)) {\n      throw new Error('Header \"forwarded\" proto parameter must be either \"http\" or \"https\".');\n    }\n  }\n\n  const xForwardedPort = getFirstHeaderValue(headers.get('x-forwarded-port'));\n  if (xForwardedPort && !VALID_PORT_REGEX.test(xForwardedPort)) {\n    throw new Error('Header \"x-forwarded-port\" must be a numeric value.');\n  }\n\n  const xForwardedProto = getFirstHeaderValue(headers.get('x-forwarded-proto'));\n  if (xForwardedProto && !VALID_PROTO_REGEX.test(xForwardedProto)) {\n    throw new Error('Header \"x-forwarded-proto\" must be either \"http\" or \"https\".');\n  }\n\n  const xForwardedPrefix = getFirstHeaderValue(headers.get('x-forwarded-prefix'));\n  if (xForwardedPrefix && !VALID_PREFIX_REGEX.test(xForwardedPrefix)) {\n    throw new Error(\n      'Header \"x-forwarded-prefix\" is invalid. It must start with a \"/\" and contain ' +\n        'only alphanumeric characters, hyphens, and underscores, separated by single slashes.',\n    );\n  }\n}\n\n/**\n * Checks if a specific proxy header is allowed.\n *\n * @param headerName - The name of the proxy header to check.\n * @param trustProxyHeaders - A set of allowed proxy headers.\n * @returns `true` if the header is allowed, `false` otherwise.\n */\nexport function isProxyHeaderAllowed(\n  headerName: string,\n  trustProxyHeaders: ReadonlySet<string>,\n): boolean {\n  return (","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/ssr/src/utils/validation.ts#L207-L243","documentation":"Angular SSR checks the trusted `x-forwarded-prefix` header against `/^\\/([a-z0-9_-]+\\/)*[a-z0-9_-]*$/i`. The value must be a path prefix starting with `/`, containing only alphanumerics, hyphens, and underscores separated by single slashes. Anything else (full URLs, query strings, double slashes, spaces) is rejected to prevent prefix injection into generated asset/base href URLs.","triggerScenarios":"A request with a trusted `x-forwarded-prefix` header value like `https://x.com/app`, `my-prefix` (no leading slash), `//app`, `/app/`, trailing content with `?`, or an absolute URL.","commonSituations":"Proxies that set X-Forwarded-Prefix to a full URL instead of a path; platform-as-a-service routers appending the deployment path with extra characters; hand-crafted test requests.","solutions":["Configure the proxy to send only the path prefix, e.g. `/my-app`.","Strip the scheme/host portion from the value at the proxy (use the path only).","Remove `x-forwarded-prefix` from `trustProxyHeaders` if you don't rewrite asset paths.","Fix double/trailing slashes: use `/app` or `/app/`-style paths matching the regex (single slashes between segments)."],"exampleFix":"// before\nproxy_set_header X-Forwarded-Prefix \"https://cdn.example.com/app\";\n// after\nproxy_set_header X-Forwarded-Prefix \"/app\";","handlingStrategy":"validation","validationCode":"const prefix = request.headers.get('x-forwarded-prefix')?.split(',')[0].trim();\nif (prefix && !/^\\/([a-z0-9_-]+\\/)*[a-z0-9_-]*$/i.test(prefix)) throw new Error(`Invalid x-forwarded-prefix: ${prefix}`);","typeGuard":null,"tryCatchPattern":"try {\n  validateHeaders(headers, allowedHosts, disableHostCheck);\n} catch (e) {\n  if ((e as Error).message.includes('x-forwarded-prefix')) {\n    return res.status(400).end('Invalid prefix header');\n  }\n  throw e;\n}","preventionTips":["Send only path prefixes (starting with /), never full URLs.","Normalize double/trailing slashes at the proxy.","Only trust x-forwarded-prefix if you deploy under a subpath."],"tags":["angular","ssr","security","proxy","validation"],"backgroundTag":"invalid-proxy-header-value","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}