{"record":{"id":"2118a0754438ac54","repo":"angular/angular-cli","slug":"header-forwarded-proto-parameter-must-be-either","errorCode":null,"errorMessage":"Header \"forwarded\" proto parameter must be either \"http\" or \"https\".","messagePattern":"Header \"forwarded\" proto parameter must be either \"http\" or \"https\"\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular/ssr/src/utils/validation.ts","lineNumber":209,"sourceCode":"  allowedHosts: ReadonlySet<string>,\n  disableHostCheck: boolean,\n): void {\n  const headers = request.headers;\n  for (const headerName of HOST_HEADERS_TO_VALIDATE) {\n    const headerValue = getFirstHeaderValue(headers.get(headerName));\n    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.',","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/ssr/src/utils/validation.ts#L191-L227","documentation":"Angular SSR validates the `forwarded` header's `proto` parameter against `/^https?$/i` when the header is trusted. If the parameter is missing-or-invalid (anything other than http/https, case-insensitive), the request is rejected. This guards against proxy-supplied `forwarded` headers containing malformed or malicious protocol values used for open-redirect or scheme-confusion attacks.","triggerScenarios":"A request carrying a `forwarded` header (e.g. `forwarded: for=1.2.3.4;proto=ftp` or `proto=HTTP/2`, or `proto=` empty) passes through validation while the `forwarded` header is included in `trustProxyHeaders`.","commonSituations":"A misconfigured reverse proxy emitting non-standard values in the forwarded proto field; manually crafted curl/test requests; a proxy that forwards the raw client-supplied `forwarded` header instead of overwriting it.","solutions":["Fix the upstream proxy to emit `proto=http` or `proto=https` in its Forwarded header.","Have the proxy overwrite (not append) client-supplied Forwarded headers.","Remove `forwarded` from `trustProxyHeaders` if you rely on `x-forwarded-proto` instead.","In tests, send a valid value like `Forwarded: for=1.2.3.4;host=example.com;proto=https`."],"exampleFix":"// before\ncurl -H 'Forwarded: for=1.2.3.4;proto=HTTP/2' http://localhost:4200/\n// after\ncurl -H 'Forwarded: for=1.2.3.4;proto=https' http://localhost:4200/","handlingStrategy":"validation","validationCode":"const fwd = request.headers.get('forwarded');\nconst proto = fwd?.match(/proto=([^;]+)/i)?.[1]?.trim();\nif (proto && !/^(http|https)$/i.test(proto)) throw new Error(`Invalid forwarded proto: ${proto}`);","typeGuard":null,"tryCatchPattern":"try {\n  validateHeaders(headers, allowedHosts, disableHostCheck);\n} catch (e) {\n  if ((e as Error).message.startsWith('Header \"forwarded\"')) {\n    return res.status(400).end('Invalid Forwarded header');\n  }\n  throw e;\n}","preventionTips":["Configure the reverse proxy to overwrite, never append, Forwarded headers.","Only trust `forwarded` from networks you control.","Include malformed-header cases in integration tests."],"tags":["angular","ssr","security","forwarded-header","proxy"],"backgroundTag":"invalid-proxy-header-value","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}