{"record":{"id":"5b936667f2b1f7d5","repo":"angular/angular-cli","slug":"header-headername-with-value-headervalue","errorCode":null,"errorMessage":"Header \"${headerName}\" with value \"${headerValue}\" contains characters that are not allowed.","messagePattern":"Header \"(.+?)\" with value \"(.+?)\" contains characters that are not allowed\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular/ssr/src/utils/validation.ts","lineNumber":146,"sourceCode":" *\n * @param headerName - The name of the header to validate (e.g., 'host', 'x-forwarded-host').\n * @param headerValue - The value of the header to validate.\n * @param allowedHosts - A set of allowed hostnames.\n * @throws Error if the header value is invalid or the hostname is not in the allowlist.\n */\nfunction verifyHostAllowed(\n  headerName: string,\n  headerValue: string,\n  allowedHosts: ReadonlySet<string>,\n): void {\n  const url = `http://${headerValue}`;\n  if (!URL.canParse(url)) {\n    throw new Error(`Header \"${headerName}\" contains an invalid value and cannot be parsed.`);\n  }\n\n  const { hostname, pathname, search, hash, username, password } = new URL(url);\n  if (pathname !== '/' || search || hash || username || password) {\n    throw new Error(\n      `Header \"${headerName}\" with value \"${headerValue}\" contains characters that are not allowed.`,\n    );\n  }\n\n  if (!isHostAllowed(hostname, allowedHosts)) {\n    throw new Error(`Header \"${headerName}\" with value \"${headerValue}\" is not allowed.`);\n  }\n}\n\n/**\n * Checks if the hostname is allowed.\n * @param hostname - The hostname to check.\n * @param allowedHosts - A set of allowed hostnames.\n * @returns `true` if the hostname is allowed, `false` otherwise.\n */\nfunction isHostAllowed(hostname: string, allowedHosts: ReadonlySet<string>): boolean {\n  if (allowedHosts.has('*') || allowedHosts.has(hostname)) {\n    return true;","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/ssr/src/utils/validation.ts#L128-L164","documentation":"After parsing the forwarded-host header value, verifyHostAllowed rejects values that contain more than a bare hostname: a non-empty path, query, hash, or credentials in 'http://<value>' indicate the header is not a plain host and could be an injection attempt, so the request is rejected.","triggerScenarios":"X-Forwarded-Host (or similar) values like 'example.com/evil', 'example.com?q=1', 'user:pass@example.com', or 'example.com#frag' — anything where pathname !== '/', or search/hash/username/password are non-empty.","commonSituations":"Proxies forwarding the full original URL in X-Forwarded-Host; attackers probing for host-header injection; misconfigured middleware copying the entire request URL into the header.","solutions":["Configure the upstream proxy to emit only the bare hostname in X-Forwarded-Host (e.g. nginx: proxy_set_header X-Forwarded-Host $host;).","Strip path/query/credentials from the header value before forwarding.","Identify the requesting client from logs and block/fix the source of the malformed header."],"exampleFix":"// before (nginx)\nproxy_set_header X-Forwarded-Host $request_uri;\n// after\nproxy_set_header X-Forwarded-Host $host;","handlingStrategy":"validation","validationCode":"const fwh = request.headers.get('x-forwarded-host');\nif (fwh) {\n  const u = new URL(`http://${fwh}`);\n  if (u.pathname !== '/' || u.search || u.hash || u.username || u.password) {\n    return new Response('Bad Request', { status: 400 });\n  }\n}","typeGuard":"function isBareHostValue(value: string): boolean {\n  const u = new URL(`http://${value}`);\n  return u.pathname === '/' && !u.search && !u.hash && !u.username && !u.password;\n}","tryCatchPattern":"try {\n  await render(request);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('contains characters that are not allowed.')) {\n    return new Response('Bad Request: invalid forwarded host', { status: 400 });\n  } throw e;\n}","preventionTips":["Configure proxies with proxy_set_header X-Forwarded-Host $host so only the bare hostname is forwarded.","Never copy the full request URI into host headers in custom middleware.","Treat violations as potential host-header-injection probes and alert/log them."],"tags":["angular","ssr","security","http-headers","host-header-injection"],"backgroundTag":"malformed-forwarded-header","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}