Nginx使用$remote_addr进行proxy_pass

75
我试图在我的proxy_pass中包含$remote_addr或$http_remote_addr,但没有成功。
重写规则有效。
location ^~ /freegeoip/ {  
  rewrite ^ http://freegeoip.net/json/$remote_addr last;
}

没有$remote_addr的proxy_pass可以正常工作,但freegeoip无法读取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;
}

然后,我会把 IP 地址添加到请求的末尾,就像这样:

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

但是nginx报告了这个错误:没有定义解析器来解析freegeoip.net


错误是在您重新启动nginx时发生的,还是在http请求命中位置块时发生的? - Rob Squires
当我请求URL时 - david.sansay
8个回答

200

如果代理传递语句中没有变量,则在启动或重新加载期间将使用"gethostbyaddr"系统调用,并将永久缓存该值。

如果有任何变量,例如使用以下任一方式:

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

然后nginx将使用内置域名解析器,因此“resolver”指令必须存在。 “resolver”可能是一个误称;可以将其视为“内置解析器将使用哪个DNS服务器”。自nginx 1.1.9以来,内置解析器将遵守DNS TTL值。在那之前,它使用了一个固定值5分钟。


11
这是一个更有用和信息丰富的回答。谢谢。 - Kevin Cantwell
2
关键是代理传递值中存在的任何变量,感谢正确的答案和解释。 - Pablo Banderas
4
一些参考资料? - deFreitas
8
有趣的事实:Docker容器的默认名称服务器为127.0.0.11,可使用命令cat /etc/resolv.conf查看。 - Richard Kiefer
2
如果运行在 Kubernetes 上,我应该将解析器设置为什么?因为没有 resolv.conf 文件。 - Arrow_Raider
显示剩余3条评论

94

看起来有点奇怪,nginx在运行时无法解析域名而不是在配置时(因为域名被硬编码)。在位置块中添加resolver声明通常可以修复运行时遇到的dns问题。因此,您的位置块可能如下所示:

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

这个解决方案基于我一段时间前读过的一篇文章 - Proxy pass and resolver。值得一读。


22
请勿使用像 8.8.8.8 这样的公共可访问 DNS 服务器。建议在安全受保护的信任本地网络中配置 DNS 服务器,以防止 DNS 欺骗。 - Tim
这应该是被接受的答案,因为它涵盖了其他情况,比如args,remote_addr等。 - onalbi
这仍然适用于最新的稳定nginx/1.10.3。因此,resolver指令需要与proxy_pass动态指令一起使用,例如proxy_pass http://$host;。虽然这个可以在没有解析器的情况下工作:server { listen 80; server_name myurl.com; location / {return 303 https://$host$request_uri;} } - stamster
在我的情况下(Google Cloud),我使用内部名称服务器169.254.169.254来通过主机名解析我的租户中的其他实例。 - Daniel Watrous

17
如果有人仍然遇到麻烦,对我来说,将proxy_pass主机移动到单独的upstream有所帮助,所以我想到了这样的东西。
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
如果您的代理URI不会改变,并且无需添加额外的解析器步骤,则这是更简单的答案! - theannouncer

1
你希望这个能够运作:
location ^~ /freegeoip/ {
  proxy_pass http://freegeoip.net/json/$remote_addr;
}

但正如 Chris Cogdon 指出的那样,它失败了,因为在运行时,nginx 没有解析域名,所以 proxy_pass 需要一个解析器(请参见 proxy_pass 文档)。
似乎有一些能够工作的方法(虽然可能是一种 hack!),就是做一些触发 nginx 在启动时解析域名并缓存的操作。
因此,更改配置以包括一个没有任何变量的位置(对于相同的主机),以及包含变量的位置:with
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;
}

主机现在将在启动时解析(其缓存值将在运行时的第二个位置中使用)。您可以通过将主机名(freegeoip.net)更改为不存在的内容来验证此启动解析行为,当您运行nginx -tnginx -s reload时,会出现紧急错误([emerg] host not found in upstream)。当然,您可以通过省略任何resolver,命中您的location块,并观察日志中没有错误来验证在运行时使用缓存值。

1

另一种方法是,您可以在proxy_pass中提供主机ip地址和port号,而不是主机URL,例如下面的示例:

您的代码: proxy_pass http://freegeoip.net/json/;

更新后的代码: proxy_pass http://10.45.45.10:2290/json/;


这对我有用,我试图代理回本地主机上的一些Docker实例,但是本地主机无法工作(由于某种原因无法解析),但是127.0.0.1完全可以工作。 - OzzyTheGiant

0

你还可以在 proxy_pass uri 中提到你的 nginx 服务器端口。这个方法解决了我的问题。


0

0
对于任何遇到这个问题的人来说,重要的是在proxy_pass之后设置代理头,然后这个问题应该就会解决了。
        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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接