Inspect and control upstream response headers in Nginx
Log selected upstream headers, hide or replace them deliberately, and account for response status, processing phase, and header inheritance.
Use $upstream_http_* variables to inspect selected upstream response headers. Use proxy_hide_header to suppress a header on the client response, and add_header to append one. Appending is not the same as replacing an upstream value.
Record selected headers
Inside http, define a format and use its name in the applicable logging context:
log_format upstream_debug '$remote_addr "$uri" status=$status '
'up_content_type="$upstream_http_content_type" '
'up_cache_control="$upstream_http_cache_control" '
'up_time="$upstream_response_time"';
access_log /var/log/nginx/upstream.log upstream_debug;
Header names become lowercase variable suffixes with hyphens replaced by underscores: X-Request-Id becomes $upstream_http_x_request_id. These variables are not a full raw capture of every header from every retry; use dedicated tracing when that is the requirement.
Compare GET response headers without downloading the body to the terminal:
curl -sS -D - -o /dev/null https://example.com/path
curl -sS -D - -o /dev/null http://127.0.0.1:8080/path
Match Host, authentication, and other relevant request conditions when comparing. curl -I sends HEAD, which an application can handle differently.
Hide, allow, or replace a header
location / {
proxy_pass http://127.0.0.1:8080;
proxy_hide_header X-Powered-By;
proxy_hide_header X-App-Version;
add_header X-App-Version "public-v1" always;
}
The hide-plus-add pair replaces the downstream representation of X-App-Version. Using only add_header could produce duplicate fields.
Nginx normally suppresses certain upstream headers, including Date, Server, and X-Accel-*. proxy_pass_header permits a field otherwise hidden, for example proxy_pass_header Date; when deliberately needed. Hiding the upstream Server does not remove Nginx's own server-identification header.
Do not treat X-Accel-Redirect as an ordinary debug field. Nginx may process it as an internal redirect. proxy_pass_header controls forwarding; proxy_ignore_headers controls processing of supported special fields. Exposing internal redirect paths is usually unnecessary.
Check status and inheritance
Without always, add_header applies only to documented status codes, including 304. Add always when the header should also appear on error responses. Empty variable values are not a way to guarantee a visible header.
By the default inheritance rule, defining any add_header at a child level prevents inheriting the parent's set. Restate required headers there when using that model. Nginx 1.29.3 and later also support add_header_inherit, including merge; check your version before using it.
DataKit log collection can retain selected header fields for cache or version investigations. Avoid collecting tokens and session cookies merely to debug an unrelated header.
FAQ
Why does the client still see the old value?
Check the matched location, duplicate fields, successful reload, and browser/CDN caching. A 304 is not automatically excluded by add_header's default status list.
Does Nginx forward Set-Cookie?
Normally yes. Cookie rewriting uses directives such as proxy_cookie_domain and proxy_cookie_path; preserve multiple cookie fields correctly rather than folding them into one generic header.
Can an upstream header control a request-side decision?
Only after that header exists. A map can be evaluated lazily in a suitable response-phase use, but a rewrite-phase if cannot inspect a response that has not arrived. For cache storage, use response-aware controls such as proxy_no_cache as documented, rather than assuming any variable works in any phase.
References: proxy response-header controls, add_header and inheritance.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan