{"record":{"id":"f2ee28e2b9a5288d","repo":"angular/angular-cli","slug":"header-x-forwarded-port-must-be-a-numeric-value","errorCode":null,"errorMessage":"Header \"x-forwarded-port\" must be a numeric value.","messagePattern":"Header \"x-forwarded-port\" must be a numeric value\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular/ssr/src/utils/validation.ts","lineNumber":215,"sourceCode":"    if (headerValue && !disableHostCheck) {\n      verifyHostAllowed(headerName, headerValue, allowedHosts);\n    }\n  }\n\n  const forwarded = headers.get('forwarded');\n  if (forwarded) {\n    const forwardedParams = parseForwardedHeader(forwarded);\n    if (forwardedParams.host && !disableHostCheck) {\n      verifyHostAllowed('Forwarded \"host\"', forwardedParams.host, allowedHosts);\n    }\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.","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/ssr/src/utils/validation.ts#L197-L233","documentation":"Angular SSR requires the trusted `x-forwarded-port` header to be purely numeric (`/^\\d+$/`). Only the first value of a comma-separated header is checked. Any non-numeric value on the first port entry causes this throw, protecting against header injection in reconstructed request URLs.","triggerScenarios":"A request includes `x-forwarded-port` (and that header is trusted) whose first value is not all digits — e.g. `443, 8080` is fine, but `x-forwarded-port: 443/TCP`, `x-forwarded-port: 443,foo` (first value fine, but `x-forwarded-port: abc`) fails, or multiple merged headers.","commonSituations":"Reverse proxies appending unusual annotations to the port; load balancers sending multiple port headers that get joined; hand-written integration tests passing malformed values.","solutions":["Configure the proxy to send a plain numeric port in x-forwarded-port (e.g. `443`).","Ensure only one x-forwarded-port header is set, set by the trusted proxy itself.","Remove `x-forwarded-port` from `trustProxyHeaders` if you do not need the port.","Fix test harnesses to send numeric-only values."],"exampleFix":"// before\nproxy_set_header X-Forwarded-Port \"$server_port/TCP\";\n// after\nproxy_set_header X-Forwarded-Port \"$server_port\";","handlingStrategy":"validation","validationCode":"const port = request.headers.get('x-forwarded-port');\nconst first = port?.split(',')[0].trim();\nif (first && !/^\\d+$/.test(first)) throw new Error(`Invalid x-forwarded-port: ${first}`);","typeGuard":null,"tryCatchPattern":"try {\n  validateHeaders(headers, allowedHosts, disableHostCheck);\n} catch (e) {\n  if ((e as Error).message.includes('x-forwarded-port')) {\n    return res.status(400).end('Invalid port header');\n  }\n  throw e;\n}","preventionTips":["Have exactly one proxy layer emit a plain numeric x-forwarded-port.","Strip non-numeric annotations at the proxy.","Only include x-forwarded-port in trustProxyHeaders if you use it."],"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"}