82

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:

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

but nginx reports this error:

no resolver defined to resolve freegeoip.net
2
  • does the error occur when you restart nginx, or when an http request hits the location block? Commented Jul 16, 2013 at 21:05
  • When I request the url Commented Jul 16, 2013 at 23:09

9 Answers 9

212

If the proxy_pass statement has no variables in it, then it will use the gethostbyaddr system call during startup 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:
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 "which DNS server will the built-in resolver use". Since NGINX 1.1.9, the built-in resolver will honor DNS TTL values. Before then it used a fixed value of 5 minutes.

Sign up to request clarification or add additional context in comments.

9 Comments

This is a much more useful and informative answer. Thank you.
The key being any variable present in the proxy_pass value, thanks for the right answer and explanation.
Some reference?
Fun fact: docker container's default nameserver is at 127.0.0.11, see cat /etc/resolv.conf.
What am I supposed to set the resolver to if it is running kubernetes? There is no resolv.conf file.
|
99

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.

4 Comments

This should be the accepted answer because it cover other cases, like args, remote_addr, etc
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;} }
In my case (Google Cloud), I used the internal nameserver, 169.254.169.254, to resolve other instances in my tenant by hostname.
17

If anyone is still experiencing trouble, for me it helped to move the proxy_pass host to a separate upstream, so I came 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/;
  }
}

1 Comment

This is the easier answer if your proxied URI does not change, and no need to add additional resolver step!
2

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.

Comments

2

Another way is to provide the host ip address and port number instead of the 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/;

1 Comment

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.
0

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

Comments

0

Try to use dig or nslookup to make sure this is still an nginx issue. In my case the issue was that one of the DNS servers 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

Comments

0

When your upstream domain name is constant, and variables are used only for the request URI, you can entirely omit any resolver-related configuration by rewriting the request URI before the proxied request is made:

location ^~ /geoip/ {
    rewrite ^ /json/$remote_addr break; # stay within the current location
    proxy_pass http://ip-api.com; # freegeoip.net doesn't provide API anymore
}

It's quite surprising that this method hasn't been mentioned in all these years. Any query arguments present in the original request will be preserved unless a question mark is explicitly added at the end of the rewrite rule. Additional query arguments can be added via the rewrite rule as well.

An alternative way to avoid the need to define a resolver (which might be more suitable in some scenarios) - by specifying a dedicated upstream - was suggested by user @AmazingTurtle.

Only when the domain name varies (e.g., if you're implementing some kind of dynamic proxy), using a resolver is indeed the only possible approach.

Comments

-1

For anyone who is dealing with this issue, its important to set the proxy header after the proxy_pass and then this should resolve the issue:

        proxy_pass http://backend-server;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Host $host;

1 Comment

This is absolutely unrelated to the OP issue. Moreover, nhe nginx config is declarative (except the directives from the rewrite module), so the directives order inside the location block usually doesn't matter (well, actually the order of filter directives such as add_header or proxy_set_header does matter in terms of generated HTTP headers order, but it shouldn't affect backend web app or user browser behavior).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.