angular/angular-cli · error · Error

Header "${headerName}" contains an invalid value and cannot

Error message

Header "${headerName}" contains an invalid value and cannot be parsed.

What it means

verifyHostAllowed interprets a forwarded-host header value (e.g. X-Forwarded-Host) by parsing it as 'http://<value>'. If the value cannot be parsed as a URL at all (it contains characters illegal in a hostname), it throws instead of trusting it. This prevents malformed header injection from reaching host validation.

Source

Thrown at packages/angular/ssr/src/utils/validation.ts:141

    : request;
}

/**
 * Validates a specific host header value against the allowed hosts.
 *
 * @param headerName - The name of the header to validate (e.g., 'host', 'x-forwarded-host').
 * @param headerValue - The value of the header to validate.
 * @param allowedHosts - A set of allowed hostnames.
 * @throws Error if the header value is invalid or the hostname is not in the allowlist.
 */
function verifyHostAllowed(
  headerName: string,
  headerValue: string,
  allowedHosts: ReadonlySet<string>,
): void {
  const url = `http://${headerValue}`;
  if (!URL.canParse(url)) {
    throw new Error(`Header "${headerName}" contains an invalid value and cannot be parsed.`);
  }

  const { hostname, pathname, search, hash, username, password } = new URL(url);
  if (pathname !== '/' || search || hash || username || password) {
    throw new Error(
      `Header "${headerName}" with value "${headerValue}" contains characters that are not allowed.`,
    );
  }

  if (!isHostAllowed(hostname, allowedHosts)) {
    throw new Error(`Header "${headerName}" with value "${headerValue}" is not allowed.`);
  }
}

/**
 * Checks if the hostname is allowed.
 * @param hostname - The hostname to check.
 * @param allowedHosts - A set of allowed hostnames.

View on GitHub (pinned to bb72145f9a)

Solutions

  1. Find the client/proxy sending the malformed header and fix or drop it (configure the proxy to sanitize X-Forwarded-Host).
  2. Send only a bare hostname (no scheme, no path) in X-Forwarded-Host.
  3. If the header is not needed, remove it at the reverse proxy so validateHeaders sees a clean request.

Example fix

// before (client request)
fetch(url, { headers: { 'X-Forwarded-Host': 'https://example.com' } });
// after
fetch(url, { headers: { 'X-Forwarded-Host': 'example.com' } });
Defensive patterns

Strategy: validation

Validate before calling

const fwh = request.headers.get('x-forwarded-host');
if (fwh && !URL.canParse(`http://${fwh}`)) {
  return new Response('Bad Request', { status: 400 });
}

Type guard

function isParsableHostHeader(value: string): boolean {
  return URL.canParse(`http://${value}`);
}

Try / catch

try {
  await render(request);
} catch (e) {
  if (e instanceof Error && e.message.includes('cannot be parsed.')) {
    return new Response('Bad Request: malformed host header', { status: 400 });
  } throw e;
}

Prevention

When it happens

Trigger: A client sends a Host-related header such as X-Forwarded-Host containing characters that make 'http://<value>' unparseable — e.g. spaces, control characters, multiple comma-joined values with junk, or '@'/'%' sequences that break URL parsing.

Common situations: Malicious or buggy proxies appending junk to X-Forwarded-Host; clients sending a full URL (including scheme) in the header; fuzzing/scanning traffic hitting a public SSR endpoint.

Related errors


AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30). Data as JSON: /api/errors/9aa52921cd1ab21f. Report an issue: GitHub.