NGINX Proxy Headers Checker
Check NGINX reverse proxy snippets for forwarded headers, trust-boundary risks, timeout and buffering settings, WebSocket cues, and remediation rows.{{ summary.heading }}
- {{ warning }}
| 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 }} |
Reverse proxy headers carry the request facts an upstream application cannot see after NGINX sits between the client and the service. The upstream may need the original host, client address chain, public scheme, public port, WebSocket upgrade intent, or the fact that a previous proxy was trusted. If those values are missing or copied from untrusted request headers, logs, redirects, cookies, OAuth callbacks, rate limits, and access decisions can all drift away from what the client actually used.
NGINX configuration makes this review more subtle than a quick search for header names. A local proxy_set_header can stop inherited header settings from applying in that context. add_header writes response headers, not upstream request headers. A WebSocket route needs different upgrade handling from an ordinary JSON route. A streaming route may need different buffering behavior from a browser app route. The same pasted block can be acceptable for one route profile and questionable for another.
Good proxy header review treats the pasted snippet as one piece of evidence, not as proof of the whole deployment. Includes, outer load balancers, real-IP configuration, NGINX version, and route behavior can change the effective result. A clean check means the pasted block matches the checked baseline for the selected assumptions; it does not prove that every upstream, include chain, or environment is configured the same way.
Technical Details:
NGINX creates a new upstream request when it proxies traffic. Some client request fields pass through by default, while fields such as Host and Connection have proxy-specific defaults unless they are explicitly set. The directives that matter most for upstream request identity are usually proxy_pass, proxy_set_header, proxy_pass_request_headers, and real-IP trust directives when NGINX sits behind another proxy.
The most important rule is context. proxy_set_header directives inherit from a previous configuration level only when the current level has no local proxy_set_header directives. Once a location or include defines one forwarded header locally, the expected Host, scheme, and client-address settings should be repeated in that same effective context or clearly supplied by another included block.
Rule Core
The checker parses semicolon-terminated NGINX directives, strips comments outside quoted strings, and evaluates the final observed directive values in the pasted block. Brace-count warnings, duplicate header warnings, and no-directive warnings are parsing signals, not proof that NGINX would reject the full configuration.
| Control Area | Main Signal | What Triggers Attention |
|---|---|---|
| Proxy route | proxy_pass, local proxy_set_header, and proxy_pass_request_headers |
Missing upstream target, disabled original request headers, or a local header set that leaves core forwarded headers absent. |
| Request identity | Host, X-Real-IP, and X-Forwarded-For |
Default Host $proxy_host, client-supplied forwarded values, missing client address chain, or custom values that need a documented upstream contract. |
| Scheme metadata | X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port, and Forwarded |
Missing scheme on HTTPS or mixed edges, raw client header relay, fixed HTTP on an HTTPS edge, or missing public host/port metadata for routes that build external URLs. |
| Trust boundary | set_real_ip_from, real_ip_header, and real_ip_recursive |
Inbound forwarding headers copied directly upstream, real-IP replacement without trusted source ranges, or X-Forwarded-For trust without recursive lookup when multi-proxy chains matter. |
| Runtime behavior | proxy_read_timeout, proxy_buffering, proxy_http_version, Upgrade, and Connection |
Read timeout below the selected target, buffering on for streaming-style routes, legacy keep-alive gaps, or missing WebSocket upgrade headers. |
| HTTPS upstream | proxy_pass https://... and proxy_ssl_server_name |
Named HTTPS upstreams without SNI enabled when the upstream certificate or virtual host selection depends on it. |
Score and Band Logic
Each check returns Pass, Warn, Fail, or Info with a severity. Fail rows subtract the full severity weight from a 100-point score, Warn rows subtract 60% of that weight rounded up, and Pass or Info rows do not subtract score.
| Signal | Boundary or Weight | How to Read It |
|---|---|---|
| Critical | 28 |
Used for missing proxy_pass and disabled original request header forwarding because the upstream route evidence is incomplete or broken. |
| High | 18 |
Used for core identity and trust failures such as missing Host/XFF/XFP, inbound header trust, WebSocket failure, or misuse of add_header. |
| Medium | 10 |
Used for single-IP forwarding, custom host/scheme values, timeout gaps, legacy keep-alive concerns, HTTPS upstream SNI, and similar operational risks. |
| Low | 5 |
Used for lower-impact cleanup such as optional host/port metadata or buffering choices that depend on route behavior. |
| Summary band | <55, <75, <90, otherwise |
The summary reads Blocked, Fix first, Review, or Ready; three or more Fail rows also force Blocked. |
The Forwarded header is treated as optional because many NGINX deployments still use the older X-Forwarded-* fields. It can improve standardization for compatible upstreams, but it carries the same trust problem as other forwarding metadata: the value is only useful when each proxy that contributes to it is trusted and configured intentionally.
Everyday Use & Decision Guide:
Start with one effective server, location, or include block for the route under review. Put a short route or ticket name in Proxy block label, choose the closest Route profile, and set Public edge scheme before reading the summary. A WebSocket block, an API route, and a server-sent events route should not be judged with the same runtime expectations.
Use NGINX proxy version to match the deployed proxy. The modern setting treats NGINX 1.29.7 or newer as having acceptable default upstream HTTP/1.1 behavior. The legacy and unknown settings expect explicit proxy_http_version 1.1 handling for keep-alive routes, with Connection cleared unless the route is intentionally upgrading to WebSocket.
- Choose Browser or app route when redirects, absolute URLs, cookies, and user-facing host/scheme metadata matter.
- Choose JSON API route for ordinary API proxying where Host, client IP chain, scheme, and timeout are the main review points.
- Choose WebSocket route when the block should forward
UpgradeandConnectionto the upstream, usually with amap $http_upgrade $connection_upgradedefinition. - Choose SSE or streaming route when response buffering can delay events or long-lived responses.
- Raise Expected read timeout above 60 seconds for long polling, WebSocket, streaming, or slow upstream responses.
- Use the sample buttons to compare a hardened baseline, a risky header pattern, and a WebSocket pattern, then replace the sample with real route evidence.
Read the parsing notes before acting on the table. A brace-count warning means the pasted block may not represent the full effective context. A duplicate header warning means the last matching header value is used for display, but the configuration may still need human review. If no semicolon-terminated directives are parsed, fix the pasted snippet before relying on any score.
Do not treat Ready as deployment approval. It means the checked rows did not find Fail or Warn signals under the selected route profile, version, scheme, and timeout target. Confirm includes, upstream framework trust settings, load balancer real-IP handling, and at least one deployed request trace before closing a proxy header review.
Step-by-Step Guide:
Review one proxy route at a time so the audit table, header map, remediation rows, and chart all describe the same NGINX context.
- Enter Proxy block label with a short name such as
/api/ production. The summary line should now name the block being reviewed. - Choose Route profile. Start with JSON API route for ordinary API proxying, then switch to WebSocket route or SSE or streaming route only when the pasted block has that runtime behavior.
- Set NGINX proxy version to NGINX 1.29.7 or newer, Before NGINX 1.29.7, or Unknown version. Legacy or unknown selection makes missing explicit HTTP/1.1 keep-alive handling more visible.
- Set Public edge scheme to HTTPS, mixed, or HTTP-only internal route. This changes how
X-Forwarded-Protois judged. - Set Expected read timeout in seconds. If the output later says proxy_read_timeout coverage is below target, either raise the directive or lower the target to match the intended route behavior.
- Paste the NGINX snippet into NGINX proxy snippet, drop a small config text capture, or use Browse conf. The source hint should show the number of parsed
proxy_set_headerdirectives. - Clear any Parsing notes before using the result in a release review. Fix mismatched braces, duplicate local header definitions, or snippets that have no semicolon-terminated directives.
- Open Proxy Header Audit and start with Fail rows, especially proxy_pass target, Original request header forwarding, Host header forwarding, X-Forwarded-For chain, and X-Forwarded-Proto.
- Open Request Header Map to compare each upstream field with the expected value for the selected profile. Use this view to catch a setting that passed as optional but still matters for your framework.
- Open Remediation Queue and clear High and Critical rows before Low cleanup rows. Use Proxy Risk Mix when you need a category view of where score was retained or lost.
Interpreting Results:
The first Fail row is more important than the numeric score. Missing proxy_pass, disabled original request headers, direct relay of $http_x_forwarded_for, $http_x_forwarded_proto, or $http_x_real_ip, and WebSocket upgrade failures should stop a release review until the effective config is corrected or the exception is documented.
| Visible Cue | Best First Reading | What to Verify Next |
|---|---|---|
| Blocked | The score is below 55 or at least three checks failed. | Fix Fail rows from Proxy Header Audit before using the route as a baseline. |
| Host Default Host $proxy_host | The upstream may receive the proxy target host instead of the public request host. | Use proxy_set_header Host $host; unless the upstream intentionally requires a fixed virtual host. |
| X-Forwarded-For from $http_x_forwarded_for | The upstream may receive a client-supplied chain without appending the trusted proxy hop. | Prefer $proxy_add_x_forwarded_for and review real-IP trust separately. |
| proxy_set_header inheritance boundary | The pasted context defines at least one local proxy header while a core forwarded header is missing. | Repeat Host, X-Forwarded-For, and X-Forwarded-Proto in the same effective context or paste the include that supplies them. |
| Proxy Risk Mix gap | One control category lost weighted score because of Warn or Fail rows. | Use the chart for triage only, then act on the specific evidence and recommended action in the table. |
Info rows are not failures. A missing RFC 7239 Forwarded header is informational because many upstream stacks still depend on X-Forwarded-* fields. Missing X-Forwarded-Host can also be informational for API-only routes. Treat Info rows as prompts to confirm intent, not as automatic remediation work.
A clean result does not mean the upstream application trusts the values correctly. Check the framework or service configuration that consumes forwarded headers, especially trusted proxy ranges, generated URL settings, secure-cookie detection, and client-IP logging.
Worked Examples:
Ordinary API route with complete forwarding
A block labeled /api/ production uses the JSON API route profile, HTTPS edge, modern NGINX selection, and a 60-second expected read timeout. The snippet includes proxy_pass http://api_backend;, proxy_set_header Host $host;, X-Real-IP $remote_addr, X-Forwarded-For $proxy_add_x_forwarded_for, X-Forwarded-Proto $scheme, X-Forwarded-Host $host, X-Forwarded-Port $server_port, and proxy_buffering on. The summary reads Ready, Request Header Map shows the expected core fields, and Remediation Queue reports no queued directives.
Client-supplied forwarding headers copied upstream
A risky browser-app block sets Host $proxy_host, X-Forwarded-For $http_x_forwarded_for, and X-Forwarded-Proto $http_x_forwarded_proto, with add_header X-Forwarded-Proto $scheme; also present. Proxy Header Audit flags Request header directive choice, Host header forwarding, X-Forwarded-For chain, X-Forwarded-Proto, and Inbound forwarded-header trust. The remediation rows point toward proxy_set_header, $host, $proxy_add_x_forwarded_for, and proxy-controlled scheme metadata.
WebSocket route missing the map
A WebSocket route includes proxy_set_header Upgrade $http_upgrade; and proxy_set_header Connection $connection_upgrade;, but the pasted snippet does not include the matching map $http_upgrade $connection_upgrade block. The WebSocket upgrade headers row returns Warn rather than Pass because the variable may be defined elsewhere, but the pasted evidence is incomplete. Paste the include that defines the map or add the map to the effective configuration before relying on the review.
Streaming timeout and buffering mismatch
A server-sent events route is checked with SSE or streaming route and Expected read timeout set to 300 seconds, but the snippet omits proxy_read_timeout and leaves proxy_buffering at the default. The table reports proxy_read_timeout coverage below target and warns on proxy_buffering behavior. Setting proxy_read_timeout 300s; and confirming whether proxy_buffering off; is appropriate for that route clears the runtime findings.
Responsible Use Note:
Proxy snippets can reveal internal hostnames, route paths, trusted proxy ranges, upstream names, timeout policy, and security gaps. Paste the smallest effective block that proves the behavior under review, and remove unrelated secrets, tokens, or private comments before sharing copied rows, JSON, CSV, chart images, or document exports.
The pasted snippet is analyzed in the browser session and no backend upload is used for the check. Shared URL state, copied output, downloaded tables, and report attachments can still carry the exact proxy evidence you entered, so handle those artifacts as configuration data.
FAQ:
Why does one local proxy_set_header make other headers matter?
NGINX inherits proxy_set_header directives only when the current context has none of its own. If the pasted location sets one header locally, the audit expects the core forwarded headers to appear in that same effective context or in pasted evidence that clearly supplies them.
Is X-Forwarded-For enough for the real client IP?
It is useful only when each proxy in the chain handles trust correctly. The checker prefers $proxy_add_x_forwarded_for for the upstream chain and separately warns when inbound forwarded headers or real-IP settings are trusted without narrow set_real_ip_from ranges.
Why does add_header fail for forwarded request metadata?
add_header writes response headers. Upstream request metadata belongs on proxy_set_header, so a line such as add_header X-Forwarded-Proto $scheme; does not send that scheme value to the proxied application.
Why does the WebSocket check ask for Upgrade and Connection?
WebSocket reverse proxying needs the upgrade intent forwarded explicitly. For the WebSocket profile, the checker expects Upgrade $http_upgrade and a suitable Connection value, usually driven by a map $http_upgrade $connection_upgrade block.
What should I do when parsing notes appear?
Treat the result as provisional. Fix brace imbalance, paste the include that defines missing context, remove unrelated text, or resolve duplicate proxy_set_header entries before using the audit rows as release evidence.
Glossary:
- Forwarded header
- An RFC 7239 request header that can carry proxy-disclosed metadata such as client address, host, and protocol for compatible upstreams.
- Real-IP trust
- The NGINX configuration that decides which previous proxy addresses may replace the apparent client address.
- Public edge scheme
- The HTTP or HTTPS scheme clients use before their request reaches the upstream application through NGINX.
- WebSocket upgrade
- The HTTP/1.1 protocol switch that requires NGINX to pass Upgrade and Connection metadata to the upstream route.
- SNI
- Server Name Indication, the TLS hostname signal an HTTPS upstream may need to select the right certificate or virtual host.
References:
- Module ngx_http_proxy_module, NGINX documentation.
- WebSocket proxying, NGINX documentation.
- Module ngx_http_realip_module, NGINX documentation.
- RFC 7239: Forwarded HTTP Extension, IETF, June 2014.