Freeshell 上的 HTTP 服务器在使用 HTTP Proxy 时,请求的来源 IP 是代理服务器的内网 IP,而请求协议也都是 HTTP(HTTPS 证书和加解密已经在 HTTP Proxy 上处理了)。要知道客户端的真实 IP 和 HTTP/HTTPS 协议,可以使用 HTTP header 中的下列字段:

  • X-Forwarded-For: 客户端 IP 地址,可能是 IPv4 或 IPv6 地址
  • X-Real-IP: 同 X-Forwarded-For
  • X-Forwarded-Proto: 客户端是通过 http 还是 https 访问的,值可能是 http 或 https
  • X-Scheme: 同 X-Forwarded-Proto

其中 X-Forwarded-Proto 是今天(2015年2月26日)新加的 HTTP Header,因为 X-Forwarded-ForX-Forwarded-Proto 是 HTTP 代理服务器的事实标准。

要让 WordPress、Discuz 等 PHP 程序能够正确识别用户的真实 IP 和 HTTP 请求协议,可以把上述 HTTP Header 作为服务器的环境变量传递给 PHP 程序。

Nginx

Freeshell 上如果安装的是 Nginx + FastCGI,可以在 nginx 配置文件中的 server 块以外添加如下代码:


map $http_x_forwarded_proto $proxy_https {
        default off;
        https on;
}

然后在 location 块内的 include fastcgi_params; 一行下面添加

fastcgi_param remote_addr $http_x_forwarded_for;
fastcgi_param https $proxy_https;

在 PHP 程序中,$_SERVER['REMOTE_ADDR'] 就会变成客户端的真实 IP;而客户端是否通过 https 来访问,可以通过 $_SERVER['HTTPS'] 是否为 on 来判断。

Apache

Freeshell 上如果安装的是 Apache 2,建议安装 mod_rpaf 模块,该模块可以根据 X-Forwarded-ForX-Forwarded-Proto 正确设置 PHP 环境变量。

如果不想安装模块,可以如下解决:

  • 客户端 IP:由于 Apache 不允许修改 REMOTE_ADDR,需要修改 PHP 代码,首先尝试 $_SERVER['X_FORWARDED_FOR'] 变量,如果存在就把它作为客户端 IP;如果不存在,再使用 $_SERVER['REMOTE_ADDR']。Discuz 已经这样做了,但 WordPress 没有这样做,需要自己改或安装相关插件。
  • 客户端 HTTP/HTTPS:在 Apache 配置文件中添加如下代码: SetEnvIf X-Forwarded-Proto https HTTPS=On

    PHP 程序中可以通过 $_SERVER['HTTPS'] 是否为 on 来判断。

感谢 崔天一 和 郑子涵 提出问题。