I'm using Nginx as a reverse proxy.
What is the difference between these headers:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
In some documents/tutorials I see both are used, in others only the first.
They seem similar, so I'd like to understand how they differ and whether I need to use both at the same time.
CodePudding user response:
What is the difference between these headers?
Did you check the $proxy_add_x_forwarded_for
variable documentation?
the
X-Forwarded-For
client request header field with the$remote_addr
variable appended to it, separated by a comma. If theX-Forwarded-For
field is not present in the client request header, the$proxy_add_x_forwarded_for
variable is equal to the$remote_addr
variable.
If the incoming request already contains the X-Forwarded-For
header, lets say
X-Forwarded-For: 203.0.113.195, 150.172.238.178
and your request is coming from the IP 198.51.100.17
, the new X-Forwarded-For
header value (to be passed to the upstream) will be the
X-Forwarded-For: 203.0.113.195, 150.172.238.178, 198.51.100.17
If the incoming request won't contain the X-Forwarded-For
header, this header will be passed to the upstream as
X-Forwarded-For: 198.51.100.17
On the other hand, the X-Real-IP
header being set the way you show in your question will be always be equal to the $remote_addr
nginx internal variable, in this case it will be
X-Real-IP: 198.51.100.17
(unless the ngx_http_realip_module will get involved to change that variable value to something other than the actual remote peer address; read the module documentation to find out all the details; this SO questions has some useful examples/additional details too.)
Whether I need to use both at the same time?
The very first your question should be "do I need to add any those headers to the request going to my backend at all?" That really depends on your backend app. Does it counts on any of those headers? Does those headers values actually makes any difference on the app behavior? How your backend app will treat those headers values? As you can see, the request origin assumed to be the very first address from the X-Forwarded-For
addresses list. On the other hand, that header can be easily spoofed, so some server setups may allow to use that header for the trusted sources only, removing it otherwise. If you set the X-Real-IP
header by your server setup, it will always contain the actual remote peer address; if you don't, and you've got a spoofed request with the X-Real-IP
header already present in it, it will be passed to your backend as is, which may be really bad if your app will prefer to rely on that header rather than X-Forwarded-For
one. Different backend apps may behave differently; you can check this GitHub issue discussion to get the idea.
Summarizing all this up.
If you definitely know what headers your backend app can actually process and how it will be done, you should set required headers according to the way they will be processed and skip non-required to minimize the proxied payload. If you don't, and you don't know if your app can be spoofed with the incorrect X-Forwarded-For
header, and you don't have a trusted proxy server(s) in front of your nginx instance, the most safe way will be to set both according to an actual remote peer address:
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
If you know for sure your backend app cannot be spoofed with the wrong X-Forwarded-For
HTTP header and you want to provide it with all the information you've got in the original request, use the example you've shown in your question:
proxy_set_header X-Forwarded-For $proxy_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
Some additional technical information.
Actually, those X-Forwarded-...
HTTP headers are some kind of non-standard headers. According to MDN, it was assumed that the standard headers for transmitting such information would be Via
, described in the RFC7230, and Forwarded
, described in the RFC7239. However the X-Forwarded-For
, X-Forwarded-Host
and X-Forwarded-Proto
became an alternative and de-facto standard version instead of those. Instead of using X-Forwarded-Host
, which may or may not be interpreted by your backend app, a more reliable approach is to explicitly set the Host
HTTP header for the proxied request using either
proxy_set_header Host $host;
or
proxy_set_header Host $http_host;
or even
proxy_set_header Host $server_name;
(you can check the difference between $host
, $http_host
and $server_name
nginx internal variables here.) On the other hand the X-Forwarded-Proto
used quite often to tell the backend app if the original request was made over the encrypted HTTPS protocol or not. Sometimes you can even see the X-Forwarded-Proxy
header used in the configuration; as for me, this one looks senseless since the backend app should not behave differently depending of the reverse proxy software you actually use; however I can believe there can be web apps that really can deal with that one in a some useful way. MDN does not mention the X-Real-IP
header at all; however that are definitely quite a lot of web apps that should be provided with that one.
One more technical detail. Like some other reverse proxy servers, nginx will "fold" multiple X-Forwarded-For
headers into a single one, so the
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
and the
proxy_set_header X-Forwarded-For $http_x_forwarded_for;
proxy_set_header X-Forwarded-For $remote_addr;
configuration fragments will behave identically, passing the single X-Forwarded-For
header to your backend app, equal no matter of what configuration will be used.