Forwarded Headers Need a Trust Boundary
Define which proxy can supply client and scheme metadata, overwrite untrusted values and keep the application listener private.
On this page
A request header is input from whoever could reach the receiver. A client can supply a field that looks like proxy metadata just as easily as it can supply a User-Agent. The field becomes useful evidence only when the application has a clear policy for trusting the sender and the proxy has a clear policy for replacing or extending the value.
Start by drawing the accepted route. For a simple deployment, one public HTTPS edge forwards to a loopback application listener. The application accepts its proxy metadata only from that edge. Direct public access to the application is blocked. Those conditions support a much simpler contract than a chain of independently managed forwarding services.
Define the header contract
List the values the application actually uses. Common examples include the requested host, original scheme and a client-address field. For each one, state who creates it, what valid values look like and what the application does when it is absent or malformed. Avoid enabling broad proxy trust just because a framework provides a convenient switch.
The canonical publication domain should come from application configuration when it is fixed. A client-supplied Host or forwarded host field should not choose the origin of RSS entries, password links or other absolute URLs. Nedan follows that fixed-identity pattern for its publication metadata.
Also inventory alternate fields. Overwriting X-Forwarded-For while allowing an untrusted Forwarded header through can leave two contradictory accounts of the same request. Determine which fields the application consumes, clear unused alternatives and make one documented interpretation authoritative.
A single-edge example
The following fragment belongs inside an already configured nginx HTTPS server block. It assumes nginx directly receives the visitor connection, has no real-IP rewrite and forwards to a loopback application. It omits the surrounding certificate and listener configuration deliberately. It is not a drop-in configuration for a CDN or a separate TLS front end.
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host nedan.cloudthe.me;
proxy_set_header X-Forwarded-Host nedan.cloudthe.me;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Forwarded "";
}Here, the public edge replaces the address metadata with the peer it actually sees. It does not preserve a client-supplied address chain. The fixed HTTPS scheme is appropriate only because this location is part of the intended HTTPS route. An HTTP redirect listener should not proxy arbitrary requests through the same fragment.
Confirm that the application reads only the agreed fields and applies the agreed sender policy. The nginx fragment alone cannot enforce how an application interprets metadata. The TLS identity note describes the separate identity questions at an edge.
Review a chain explicitly
A trusted front proxy changes the model. The socket peer at nginx can now be the front proxy rather than the visitor. A client-address chain is useful only when every accepted hop has a known behavior and direct bypass paths are closed. Do not choose the leftmost address merely because it looks like a public visitor address.
Work backward from the receiver's actual peer through the known trusted hops. Stop at the boundary where the sender is no longer trusted to describe earlier peers. The implementation must account for the chain syntax, accepted address formats and the concrete framework's behavior; a hand-drawn arrow is not a parser.
nginx real-IP processing can change the value later seen as remote_addr. Review its trusted-sender configuration and recursive handling before reusing that variable as evidence. Broadly trusting all internet addresses defeats the intended boundary. Keep the original connection peer available in restricted diagnostic records when it helps distinguish the front proxy from its supplied metadata.
Block the bypass route
If the application accepts trusted proxy headers from arbitrary public connections, an attacker can skip the trusted edge and supply those headers directly. A loopback bind and a loopback-only Docker publication reduce that route in a single-host design. Verify the actual host listener and firewall; a Compose file describes intent, not the current host state.
Container networking can add another reachable path. Inspect the networks attached to the application and the peers that can reach its listener. The security claim should match that actual topology. A private network shared with many unrelated containers is a different trust assumption from one dedicated to a single proxy and application.
The same principle applies to a PROXY-protocol listener. Only an intended trusted front end should be able to supply that connection metadata. It is not a header that can safely be accepted on an arbitrary public application port.
Test the contract, not a lucky request
Create a bounded read-only check with deliberately conflicting Host, forwarded host, scheme and address fields. Observe the application's externally visible behavior and restricted server log. The acceptance condition is specific: spoofed metadata must not change the fixed publication origin or bypass the intended sender policy.
Repeat through the intended trusted route and, where permitted, inspect the private listener from the host. Do not expose that listener temporarily for a public test. Keep the distinction between an application metadata check and enforcement by a firewall or container runtime.
Finish the review with the accepted sender, overwritten fields, blocked bypass routes and untested conditions. This small contract is easier to maintain than a claim that all forwarded headers are safe because a reverse proxy exists somewhere in front of the service.
References
Primary module behavior: nginx proxy module and nginx real-IP module. The trust contract and single-edge fragment are illustrative review material.
Continue with hop-by-hop timeout diagnosis.