http-libs
↑↓ navigate select esc close
GitHub

Forwarded Headers errors and diagnostics

Malformed input and trust boundaries

Policy Invalid whole-field syntax Invalid/disallowed value in a considered hop
Ignore (default) Continue with request properties and forwarding headers unchanged Stop before that hop; apply any fully validated nearer hops
Reject HTTP 400, no next middleware, no forwarding mutations HTTP 400, no next middleware, no forwarding mutations, including staged nearer hops

Enable strict handling with options.MalformedHeaderBehavior = MalformedHeaderBehavior.Reject;. Rejection does not echo the raw header in a diagnostic response body. Malformed hops are never skipped in favor of more distant claims. A disallowed host stops its whole hop, rather than falling back to a farther host.

Unknown extensions, missing optional values, unknown/obfuscated identities, untrusted peers, and hop limits are not malformed input. Semantic values beyond a trust or hop boundary are not validated. Whole-field syntax must still parse before individual element boundaries can be consumed safely, so a syntax error in a farther element can invalidate the field. Disabled parameter values are not semantically validated, except for, which is needed for trust traversal.

Consumption, originals, and diagnostics

Accepted elements are removed from the right of Forwarded; an unconsumed prefix is retained without reordering parameters or discarding unknown extensions. The header is removed when completely consumed. Enabled applied values preserve their pre-forwarding originals in the configurable X-Original-* headers using ASP.NET Core conventions. Inbound original-value headers are not trusted provenance.

var feature = context.Features.Get<IForwardedFeature>();
if (feature is not null)
{
var physicalPeer = feature.OriginalRemoteIpAddress;
var acceptedCount = feature.AcceptedHops.Count;
var outcome = feature.StopReason;
var rejected = feature.Rejected;
}

IForwardedFeature captures immutable original remote IP/port, scheme, host, and PathBase independently of inbound headers. AcceptedHops is nearest-first and contains only accepted elements (empty on rejection), not the entire raw chain. Its unselected parameters remain metadata, not semantically validated claims. The feature also marks processing so re-execution or repeated middleware registration cannot consume another batch of hops. It is absent when processing is disabled or the header is absent. Structured log events describe failures and boundaries without logging full headers or identifying values by default.

See deployment safety before exposing diagnostics or interpreting proxy claims as identity.