74

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

david.sansay
  • 1,329
  • 1
  • 12
  • 13

7 Answers7

195

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.

nbari
  • 25,603
  • 10
  • 76
  • 131
Chris Cogdon
  • 7,481
  • 5
  • 38
  • 30
91

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.

Rob Squires
  • 2,108
  • 16
  • 15
  • 21
    Do **NOT** use a publicly accessible DNS server such as `8.8.8.8`. [To prevent DNS spoofing, it is recommended configuring DNS servers in a properly secured trusted local network.](http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver) – Tim Oct 07 '16 at 20:12
  • This should be the accepted answer because it cover other cases, like args, remote_addr, etc – onalbi Jan 20 '18 at 11:01
  • This is still valid with latest stable nginx/1.10.3. So `resolver` directive is required in conjunction with `proxy_pass` dynamic directive e.g. `proxy_pass http://$host;`. While this one works w/o resolver: ```server { listen 80; server_name myurl.com; location / {return 303 https://$host$request_uri;} } ``` – stamster May 09 '18 at 16:21
  • In my case (Google Cloud), I used the internal nameserver, 169.254.169.254, to resolve other instances in my tenant by hostname. – Daniel Watrous Feb 09 '19 at 03:41
17

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/;
  }
}
AmazingTurtle
  • 1,187
  • 1
  • 15
  • 31
1

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/;

Rohit Gaikwad
  • 3,677
  • 3
  • 17
  • 40
  • This worked for me, was trying to proxy back to some Docker intstances on localhost, but localhost didn't work (couldn't be resolved for whatever reason) but 127.0.0.1 worked just fine. – OzzyTheGiant Jul 17 '22 at 15:14
1

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 resolver, hitting your location block, and observing no errors in the logs.

Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
Peter W
  • 952
  • 8
  • 18
0

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

RijulB
  • 565
  • 1
  • 4
  • 10
0

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.

Nginx does not re-resolve DNS names in Docker

Piotr
  • 317
  • 1
  • 13