【问题标题】:Nginx: broken_header with proxy_protocol and ELBNginx:broken_header 与 proxy_protocol 和 ELB
【发布时间】:2025-12-10 13:30:02
【问题描述】:

我正在尝试在我的 nginx 配置中设置 proxy_protocol。我的服务器位于 AWS 负载均衡器 (ELB) 后面,并且我已经为端口 80 和 443 启用了代理协议。

但是,这是我在访问服务器时得到的结果:

broken header: "��/��
                                                             '���\DW�Vc�A{����
                                                                              �@��kj98���=5���g@32ED�</A
    " while reading PROXY protocol, client: 172.31.12.223, server: 0.0.0.0:443

这是从 nginx 错误日志中直接复制粘贴 - 奇怪的字符等等。

这是我的 nginx 配置中的一个片段:

server {
  listen  80 proxy_protocol;
  set_real_ip_from 172.31.0.0/20; # Coming from ELB
  real_ip_header proxy_protocol;
  return  301 https://$http_host$request_uri;
}

server {
  listen      443 ssl proxy_protocol;
  server_name *.....com
  ssl_certificate      /etc/ssl/<....>;
  ssl_certificate_key  /etc/ssl/<....?;
  ssl_prefer_server_ciphers On;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4;
  ssl_session_cache shared:SSL:10m;
  add_header X-Frame-Options DENY;
  add_header X-Content-Type-Options nosniff;
  ssl_stapling on;
  ssl_stapling_verify on;


  ...

我在网上找不到有关此问题的任何帮助。其他人也遇到过错误的标头问题,但标头错误的错误始终是可读的——它们看起来不像对我来说是经过编码的。

有什么想法吗?

【问题讨论】:

  • 看起来这是在 443 上。它在端口 80 上如何做?另外,ELB 是否处于 HTTPS(不是 TCP)模式?
  • 端口 80 由第一个块处理 - 它只是重定向到 https。它处于 TCP/SSL 模式,因此 websockets 工作。
  • 同样的问题。您是否偶然使用了 Kubernetes?我正在尝试将其设置到 Kubernetes 集群中,并认为 kube-proxy 可能与它有关。
  • 我遇到了同样的问题。我尝试了我能想到的一切,但没有解决方案。有人有更新吗?

标签: amazon-web-services nginx amazon-elb


【解决方案1】:

两个建议:

  1. 确认您的 ELB 侦听器已配置为使用 TCP 作为协议,而不是 HTTP。我有一个 LB 配置,如下所示,它通过 proxy_protocol 配置路由到 Nginx:

    {
      "LoadBalancerName": "my-lb",
      "Listeners": [
         {
          "Protocol": "TCP",
          "LoadBalancerPort": 80,
          "InstanceProtocol": "TCP",
          "InstancePort": 80
        }
      ],
      "AvailabilityZones": [
        "us-east-1a",
        "us-east-1b",
        "us-east-1d",
        "us-east-1e"
      ],
      "SecurityGroups": [
         "sg-mysg"
      ]
    }
    
  2. 您提到您已在 ELB 中启用代理协议,所以我假设您已关注 AWS setup steps。如果是这样,那么 ELB 应该 使用第一行正确地制作 HTTP 请求,例如 PROXY TCP4 198.51.100.22 203.0.113.7 35646 80\r\n。但是,如果 HTTP 请求没有通过 PROXY ... 行进入 Nginx,那么它可能会导致您看到的问题。如果您直接在浏览器中点击 EC2 DNS 名称,或者您 ssh 进入 EC2 实例并尝试curl localhost 之类的操作,您可以重现该问题,那么您应该会在 Nginx 日志中看到类似的损坏标头错误。

要了解它是否适​​用于格式正确的 HTTP 请求,您可以使用 telnet:

    $ telnet localhost 80
    PROXY TCP4 198.51.100.22 203.0.113.7 35646 80
    GET /index.html HTTP/1.1
    Host: your-nginx-config-server_name
    Connection: Keep-Alive

然后检查 Nginx 日志,看看是否有相同的损坏标头错误。如果不是,则 ELB 可能不会发送格式正确的 PROXY 请求,我建议重新进行 ELB 代理协议配置,可能使用新的 LB,以验证其设置是否正确。

【讨论】:

  • 我遇到了同样的问题,这个答案帮助我解决了这个问题。在我的例子中,ELB 设置为使用 HTTP/HTTPS 协议与 ngnix 入口路由器通信。然后在 nginx 中打开代理模式时会中断。修复是使用 TCP/SSL。
【解决方案2】:

我也遇到了这个不可读的标题问题,这里是原因以及我如何解决它。

在我的例子中,Nginx 正确配置了use-proxy-protocol=true。它抱怨标头损坏仅仅是因为 AWS ELB 根本没有添加所需的标头(例如PROXY TCP4 198.51.100.22 203.0.113.7 35646 80)。 Nginx 直接看到加密的 HTTPS 有效负载。这就是为什么它会打印出所有不可读的字符。

那么,为什么 AWS ELB 没有添加 PROXY 标头?原来我在命令中使用了错误的端口来启用代理协议策略。应使用实例端口而不是 80 和 443。

ELB 有以下端口映射。

80 -> 30440
443 -> 31772

命令应该是

aws elb set-load-balancer-policies-for-backend-server \
  --load-balancer-name a19235ee9945011e9ac720a6c9a49806 \
  --instance-port 30440 \
  --policy-names ars-ProxyProtocol-policy

aws elb set-load-balancer-policies-for-backend-server \
  --load-balancer-name a19235ee9945011e9ac720a6c9a49806 \
  --instance-port 31272 \
  --policy-names ars-ProxyProtocol-policy

但是我用错了80和443。

希望这对某人有所帮助。

【讨论】:

    【解决方案3】:

    我遇到过类似的情况,nginx 开启了“proxy_protocol”,但 AWS ELB 设置未开启,所以我收到了类似的消息。

    编辑设置以将其打开的解决方案:

    【讨论】:

      【解决方案4】:

      我遇到了这个错误并遇到了这张票:

      这最终让我发现我的nginx.conf 文件中有一个不需要的proxy_protocol 声明。我删除了它,一切都恢复了。

      奇怪的是,nginx 版本 1.8.0 一切正常,但是当我升级到 nginx 版本 1.8.1 时,我开始看到错误。

      【讨论】:

      • 请注意,这基本上只是禁用代理协议标头解析(因此没有 X-Forwarded* 标头)...
      【解决方案5】:

      Stephen Karger 的上述解决方案是正确的,您必须调整确保配置您的 ELB 以支持代理。这是用于执行此操作的 AWS 文档:http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/enable-proxy-protocol.html。文档起初有点令人生畏,所以如果您愿意,可以直接跳到Enable Proxy Protocol Using the AWS CLI 部分下的步骤 3 和 4。这些是启用代理通道的唯一必要步骤。此外,正如 Stephen 还建议的那样,您必须确保您的 ELB 使用的是 TCP 而不是 httphttps,因为这两者都不会在 ELB 的代理实现中正常运行。我建议将您的套接字通道从 80 和 443 等常用端口移开,这样您仍然可以使用它们的默认行为保持这些标准化连接。当然,进行该调用完全取决于您的应用程序堆栈的外观。

      如果有帮助,您可以使用 npmwscat 来调试您的 websocket 连接,如下所示:

      $ npm install -g wscat
      $ wscat --connect 127.0.0.1
      

      如果连接在本地工作,那么肯定是您的负载平衡器。但是,如果没有,则几乎可以肯定您的套接字主机存在问题。

      此外,nmap 之类的工具将帮助您发现开放端口。一个不错的调试清单:

      npm install -g wscat
      # can you connect to it from within the server?
      ssh ubuntu@69.69.69.69
      wscat -c 127.0.0.1:80
      # can you connect to it from outside the server?
      exit
      wscat -c 69.69.69.69:80
      # if not, is your socket port open for business?
      nmap 69.69.69.69:80
      

      您还可以在服务器中使用 nmap 来发现开放端口。要在 ubuntu 上安装 nmap,只需 sudo apt-get install nmap。在 osx 上,brew install nmap

      这是我拥有的一个工作配置,尽管它目前不提供 ssl 支持。在这种配置中,我有 port 80 为我的 rails 应用程序提供数据,port 81 通过我的 elb 提供一个套接字连接,并且 port 82 为内部开放套接字连接。希望这对某人有帮助!任何使用 Rails、unicorn 和 Faye 进行部署的人都会发现这很有帮助。 :) 快乐的黑客!

      # sets up deployed ruby on rails server
      upstream unicorn {
        server unix:/path/to/unicorn/unicorn.sock fail_timeout=0;
      }
      
      # sets up Faye socket
      upstream rack_upstream {
        server 127.0.0.1:9292;
      }
      
      # sets port 80 to proxy to rails app
      server {
        listen 80 default_server;
        keepalive_timeout 300;
        client_max_body_size 4G;
        root /path/to/rails/public;
        try_files $uri/index.html $uri.html $uri @unicorn;
      
        location @unicorn {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header Host $http_host;
          proxy_set_header X-Forwarded_Proto $scheme;
          proxy_redirect off;
          proxy_pass http://unicorn;
          proxy_read_timeout 300s;
          proxy_send_timeout 300s;
        }
      
        error_page 500 502 503 504 /500.html;
        location = /500.html {
          root /path/to/rails/public;
        }
      }
      
      # open 81 to load balancers (external socket connection)
      server {
        listen 81 proxy_protocol;
        server_name _;
        charset UTF-8;
        location / {
          proxy_pass http://rack_upstream;
          proxy_redirect off;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection "upgrade";
        }
      }
      
      # open 82 to internal network (internal socket connections)
      server {
        listen 82;
        server_name _;
        charset UTF-8;
        location / {
          proxy_pass http://rack_upstream;
          proxy_redirect off;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection "upgrade";
        }
      }
      

      【讨论】: