angular/angular-cli · error · Error
Header "x-forwarded-proto" must be either "http" or "https".
Error message
Header "x-forwarded-proto" must be either "http" or "https".
What it means
Angular SSR validates the trusted `x-forwarded-proto` header against `/^https?$/i` (case-insensitive, first value only). Values like `https, http`, `wss`, or empty strings fail. This ensures the scheme used to rebuild absolute URLs in SSR is a safe http/https value.
Source
Thrown at packages/angular/ssr/src/utils/validation.ts:220
const forwarded = headers.get('forwarded');
if (forwarded) {
const forwardedParams = parseForwardedHeader(forwarded);
if (forwardedParams.host && !disableHostCheck) {
verifyHostAllowed('Forwarded "host"', forwardedParams.host, allowedHosts);
}
if (forwardedParams.proto && !VALID_PROTO_REGEX.test(forwardedParams.proto)) {
throw new Error('Header "forwarded" proto parameter must be either "http" or "https".');
}
}
const xForwardedPort = getFirstHeaderValue(headers.get('x-forwarded-port'));
if (xForwardedPort && !VALID_PORT_REGEX.test(xForwardedPort)) {
throw new Error('Header "x-forwarded-port" must be a numeric value.');
}
const xForwardedProto = getFirstHeaderValue(headers.get('x-forwarded-proto'));
if (xForwardedProto && !VALID_PROTO_REGEX.test(xForwardedProto)) {
throw new Error('Header "x-forwarded-proto" must be either "http" or "https".');
}
const xForwardedPrefix = getFirstHeaderValue(headers.get('x-forwarded-prefix'));
if (xForwardedPrefix && !VALID_PREFIX_REGEX.test(xForwardedPrefix)) {
throw new Error(
'Header "x-forwarded-prefix" is invalid. It must start with a "/" and contain ' +
'only alphanumeric characters, hyphens, and underscores, separated by single slashes.',
);
}
}
/**
* Checks if a specific proxy header is allowed.
*
* @param headerName - The name of the proxy header to check.
* @param trustProxyHeaders - A set of allowed proxy headers.
* @returns `true` if the header is allowed, `false` otherwise.
*/View on GitHub (pinned to bb72145f9a)
Solutions
- Configure the outermost proxy to overwrite x-forwarded-proto with exactly `http` or `https`.
- Ensure only one proxy layer sets the header instead of appending.
- Remove `x-forwarded-proto` from `trustProxyHeaders` if unused.
- Fix load balancer rules (e.g. AWS ALB listeners) that inject non-standard values.
Example fix
// before proxy_set_header X-Forwarded-Proto "$scheme, https"; // after proxy_set_header X-Forwarded-Proto "$scheme";
Defensive patterns
Strategy: validation
Validate before calling
const proto = request.headers.get('x-forwarded-proto')?.split(',')[0].trim();
if (proto && !/^(http|https)$/i.test(proto)) throw new Error(`Invalid x-forwarded-proto: ${proto}`); Try / catch
try {
validateHeaders(headers, allowedHosts, disableHostCheck);
} catch (e) {
if ((e as Error).message.includes('x-forwarded-proto')) {
return res.status(400).end('Invalid proto header');
}
throw e;
} Prevention
- Set X-Forwarded-Proto with a single literal value at the outermost proxy.
- Avoid proxies that append comma-joined protocol lists.
- Verify header values with curl after each proxy/config change.
When it happens
Trigger: A request with a trusted `x-forwarded-proto` header whose first value is not `http` or `https` — e.g. `HTTP/2, https`, `wss`, or a comma-joined list where the first entry is invalid.
Common situations: CDNs/load balancers appending protocol lists; WebSocket terminators emitting `wss`; double proxying where the header accumulates values; stale proxies emitting `HTTP/1.1`.
Related errors
- Header "x-forwarded-port" must be a numeric value.
- Header "x-forwarded-prefix" is invalid. It must start with a
- Header "${headerName}" with value "${headerValue}" is not al
- Header "forwarded" proto parameter must be either "http" or
- "${TRUST_ALL_PROXY_HEADERS}" is not allowed as a value for t
AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30).
Data as JSON: /api/errors/21e8cf1e37a9d4d7.
Report an issue: GitHub.