NGINX Proxy Headers Checker
Audit an NGINX proxy block for unsafe forwarded headers and trust-boundary gaps with checks for WebSocket, timeout and upstream TLS risks.| Check | Status | Severity | Observed | Evidence | Recommended action | Copy |
|---|---|---|---|---|---|---|
| {{ row.check }} | {{ row.status }} | {{ row.severity }} | {{ row.observed }} | {{ row.evidence }} | {{ row.action }} |
| Upstream field | Observed value | Expected for profile | Signal | Copy |
|---|---|---|---|---|
| {{ row.field }} | {{ row.observed }} | {{ row.expected }} | {{ row.signal }} |
| Priority | Directive | Suggested edit | Reason | Copy |
|---|---|---|---|---|
| {{ row.priority }} | {{ row.directive }} | {{ row.edit }} | {{ row.reason }} |
A reverse proxy sits between an untrusted request and an application that may use forwarded metadata as if it were fact. The headers NGINX sends upstream can decide which hostname an application serves, whether it believes the request was HTTPS, which address appears in audit logs, and where redirects or OAuth callbacks point.
Forwarding is therefore a trust-boundary decision, not a matter of copying every incoming header. A client can supply its own X-Forwarded-For, X-Real-IP, or X-Forwarded-Proto. At a public edge, NGINX normally needs to rebuild those values from proxy-controlled variables. When another load balancer is in front, only declared proxy addresses should be trusted to replace the client address.
| Metadata | Application use | Failure when wrong |
|---|---|---|
| Host | Virtual-host routing and public URL construction | Wrong tenant, domain, or absolute link |
| Client address | Logs, rate limits, and access policy | Spoofed identity or loss of the proxy chain |
| Scheme and port | Secure cookies, redirects, and callbacks | HTTP links or redirect loops behind TLS termination |
| Upgrade fields | WebSocket protocol switch | Ordinary HTTP response instead of a tunnel |
| Timeout and buffering | Long responses and low-latency streams | Premature disconnects or delayed delivery |
NGINX configuration is also scope-sensitive. Once a configuration level defines any proxy_set_header directive, it no longer inherits the parent set as a collection. A location that changes one header can therefore lose other headers the operator assumed came from an outer block.
A static audit can expose risky values and missing context, but it cannot prove which block receives a live request. The effective configuration, loaded includes, listener selection, upstream behavior, and a real request trace still decide what reaches the application.
How to Use This Tool:
Audit the smallest complete configuration context that contains the proxied route and the headers it inherits or replaces.
- Paste or load the effective NGINX proxy block. Include relevant
map, real-IP, and header directives when they live outside the location. - Choose the Route profile, deployed NGINX version band, and public edge scheme. These choices change the WebSocket, buffering, keep-alive, and scheme expectations.
- Set the expected read inactivity timeout for the upstream. The audit compares it with
proxy_read_timeout, using the NGINX default of 60 seconds when the directive is absent. - Review failing rows before warnings, then use the remediation queue to identify the directive and reason for each proposed change.
- Test the edited configuration with
nginx -tand a request that matches the real hostname and route. A clear static report is not a runtime test.
Interpreting Results:
Start with the verdict and fail count, then inspect the evidence behind each row. Blocked and Fix first indicate modeled failures or a low weighted score. Review still contains warnings or falls short of the ready threshold. Ready means the pasted text met this bounded rule set; it does not certify the deployed server.
Do not accept a header merely because it exists. Check that its value comes from the right trust source. In particular, raw inbound forwarding headers can look complete while allowing a client to influence the upstream's view of its address or scheme.
Technical Details:
NGINX redefines upstream request fields with proxy_set_header. The default upstream Host is $proxy_host, so preserving the normalized public hostname usually requires an explicit Host $host. The response-header directive add_header cannot perform this job.
Rule Core
The audit applies a fixed set of routing, identity, scheme, trust, and runtime checks. The most consequential conditions are summarized below.
| Area | Safer baseline | Important fail or warning condition |
|---|---|---|
| Routing | A visible proxy_pass target and request-header forwarding left on | No target, proxy_pass_request_headers off, or forwarded request fields set with add_header |
| Inheritance | Host, X-Forwarded-For, and X-Forwarded-Proto visible together when local header overrides exist | A partial local set that replaces assumed inherited headers |
| Host | $host for the public host unless the upstream contract requires another value | $proxy_host loses the public host; $http_host can be empty and retains a client-supplied port |
| Client chain | $proxy_add_x_forwarded_for and a proxy-controlled single-address value | Relaying $http_x_forwarded_for or $http_x_real_ip directly |
| Real-IP trust | Narrow set_real_ip_from ranges paired with real_ip_header | Replacement enabled without trusted sources; multi-hop X-Forwarded-For may also need real_ip_recursive on |
| Scheme | $scheme, or a documented fixed HTTPS value at a TLS-only trusted edge | Raw inbound scheme metadata or a fixed value that conflicts with the selected edge |
| WebSocket | Upgrade $http_upgrade and a valid Connection upgrade value; a referenced mapping must be present | Missing hop-by-hop upgrade forwarding or an unseen $connection_upgrade map |
| Runtime | Read timeout at least equal to the selected inactivity target; buffering off for low-latency profiles when appropriate | Short timeout, ordinary buffering on a streaming profile, or buffering off on an ordinary route without reason |
| Upstream protocol | HTTP/1.1 or newer for keep-alive and WebSocket work | HTTP/1.0, or no explicit 1.1 setting on pre-1.29.7 or unknown NGINX |
| HTTPS upstream | proxy_ssl_server_name on when a named upstream needs TLS Server Name Indication | HTTPS proxy target with SNI left off |
X-Forwarded-Host and X-Forwarded-Port are judged according to route and edge context because not every upstream reconstructs public URLs. The standardized Forwarded field is informational; it does not replace X-Forwarded compatibility unless every consumer supports it.
Formula Core
Each failing check loses its full severity weight. A warning loses half. Passing and informational checks lose no points.
| Item | Exact rule |
|---|---|
| Severity weights | Critical 28, High 18, Medium 10, Low 5, Info 0 |
| Penalty | Fail = full weight; Warn = half weight; Pass or Info = 0 |
| Blocked | At least 3 failures or score < 55 |
| Fix first | Otherwise, at least 1 failure or score < 75 |
| Review | Otherwise, at least 1 warning or score < 90 |
| Ready | No earlier verdict condition applies |
Timeout values accept milliseconds, seconds, minutes, hours, or days and are converted to seconds before comparison. The 60-second default is an inactivity timeout between successive reads, not a limit on the complete response duration.
Limitations and Privacy Notes:
The pasted configuration is parsed in the browser and is not sent to a checking service. Keep secrets out of any configuration you later copy, export, or share.
- The parser reads directives from up to 1 MiB of text, strips comments, and uses the last visible occurrence. It does not evaluate NGINX block scope, variables, includes, templates, or generated configuration.
- The result does not run
nginx -t, load modules, contact the upstream, or prove which server and location match a request. - Trust ranges, TLS termination, proxy protocol, and application framework settings must be reviewed in their effective deployment context.
References:
- NGINX HTTP proxy module, NGINX.
- NGINX HTTP real-IP module, NGINX.
- WebSocket proxying, NGINX.
- How to configure Nginx as a reverse proxy, Simplified Guide.