Nginx proxy_pass with $remote_addr

NginxProxypass

Nginx Problem Overview


I'm trying to include $remote_addr or $http_remote_addr on my proxy_pass without success.

The rewrite rule works

location ^~ /freegeoip/ {  
  rewrite ^ http://freegeoip.net/json/$remote_addr last;
}

The proxy_pass without the $remote_addr works, but freegeoip does not read the x-Real-IP

location ^~ /freegeoip/ {
  proxy_pass http://freegeoip.net/json/;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $host;
}

Then, I'm adding the ip to the end of the request, like this:

location ^~ /freegeoip/ {
  proxy_pass http://freegeoip.net/json/$remote_addr;
}

but nginx report this error: no resolver defined to resolve freegeoip.net

Nginx Solutions


Solution 1 - Nginx

If the proxy_pass statement has no variables in it, then it will use the "gethostbyaddr" system call during start-up or reload and will cache that value permanently.

if there are any variables, such as using either of the following:

set $originaddr http://origin.example.com;
proxy_pass $originaddr;
# or even
proxy_pass http://origin.example.com$request_uri;

Then nginx will use a built-in resolver, and the "resolver" directive must be present. "resolver" is probably a misnomer; think of it as "what DNS server will the built-in resolver use". Since nginx 1.1.9 the built-in resolver will honour DNS TTL values. Before then it used a fixed value of 5 minutes.

Solution 2 - Nginx

It seems a bit strange that nginx is failing to resolve the domain name at runtime rather than at configuration time (since the domain name is hard coded). Adding a resolver declaration to the location block usually fixes dns issues experienced at runtime. So your location block might look like:

location ^~ /freegeoip/ {
  #use google as dns
  resolver 8.8.8.8;
  proxy_pass http://freegeoip.net/json/$remote_addr;
}

This solution is based on an article I read a while back - Proxy pass and resolver. Would be worth a read.

Solution 3 - Nginx

If anyone is stll experiencing trouble, for me it helped to move the proxy_pass host to a seperate upstream, so I come up with something like this

upstream backend-server {
  server backend.service.consul;
}

server {
  listen       80;
  server_name  frontend.test.me;

  location ~/api(.*)$  {
    proxy_pass http://backend-server$1;
  }
  location / {
    # this works mystically! backend doesn't...
    proxy_pass http://frontend.service.consul/;
  }
}

Solution 4 - Nginx

You could also mention your nginx server port in the proxy_pass uri. This solved the issue for me.

Solution 5 - Nginx

Another way is that you can provide your host ip address and port number instead of host URL in proxy_pass like below:

Your Code: proxy_pass http://freegeoip.net/json/;

Updated Code proxy_pass http://10.45.45.10:2290/json/;

Solution 6 - Nginx

Try to use dig / nslookup to make sure this is still nginx issue. In my case the issue was that one of dns server between my nginx and root dns was not respecting TTL values that are as small as 1 second.

https://stackoverflow.com/questions/67439712/nginx-does-not-re-resolve-dns-names-in-docker/70654684#70654684

Solution 7 - Nginx

So you want this to work:

location ^~ /freegeoip/ {
  proxy_pass http://freegeoip.net/json/$remote_addr;
}

but as pointed out by Chris Cogdon, it fails because at run-time, nginx does not have the domain name resolved, and so proxy_pass requires a resolver (see also, proxy_pass docs).

Something that seems to work (though may be a hack!) is to do something that triggers nginx to resolve the domain name at start-up and cache this.

So, change the config to include a location (for the same host) without any variables (as well as your location with variables):

location = /freegeoip/this-location-is-just-a-hack {
  # A dummy location. Use any path, but keep the hostname.
  proxy_pass http://freegeoip.net/this-path-need-not-exist;
}

location ^~ /freegeoip/ {
  proxy_pass http://freegeoip.net/json/$remote_addr;
}

The host will now be resolved at start-up (and its cached value will be used in the second location at run-time). You can verify this start-up resolution behavior by changing hostname (freegeoip.net) to something that doesn't exist, and when you run nginx -t or nginx -s reload you'll have an emergency error ([emerg] host not found in upstream). And of course you can verify that the cached value is used at run-time by leaving out any resovler, hitting your location block, and observing no errors in the logs.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiondavid.sansayView Question on Stackoverflow
Solution 1 - NginxChris CogdonView Answer on Stackoverflow
Solution 2 - NginxRob SquiresView Answer on Stackoverflow
Solution 3 - NginxAmazingTurtleView Answer on Stackoverflow
Solution 4 - NginxRijulBView Answer on Stackoverflow
Solution 5 - NginxRohit GaikwadView Answer on Stackoverflow
Solution 6 - NginxPiotrView Answer on Stackoverflow
Solution 7 - NginxPeter WView Answer on Stackoverflow