Zulip 部署在反向代理后面时如何配置信任代理和转发头
【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip
如果你的 Zulip 服务器不直接暴露给公网,而是要放在 nginx、Apache2 或 HAProxy 等反向代理之后对外提供服务,就需要完成两件事:让 Zulip 信任代理通过X-Forwarded-For上报的真实客户端 IP(否则服务端日志和通知邮件里的 IP 全是代理的地址),以及让 Zulip 信任代理通过X-Forwarded-Proto上报的协议(否则 Django 的 CSRF 检查和 HTTPS 重定向会出错)。Zulip 默认不信任任何X-Forwarded-*头,因为那样会允许客户端伪造任意 IP 地址,甚至谎称连接是加密的;你需要明确告诉 Zulip 哪些 IP 地址是你的入口代理。
本文对应 Zulip 反向代理文档 中的配置步骤,适用前提是使用标准安装脚本安装的 Zulip(即服务器上存在/home/zulip/deployments/current),并且你有 root 权限。使用 Docker 部署时文档建议使用等价的LOADBALANCER_IPS/TRUST_GATEWAY_IP机制,参见 Docker 文档。
第一步:确定代理的 IP 地址
从 Zulip 主机视角确定所有反向代理的 IP 地址。注意这未必是代理的公网 IP——取决于你的网络结构,Zulip 主机看到的可能是内网地址。也允许使用 CIDR 网段表示地址范围(例如192.168.0.0/24),这在多节点或云负载均衡场景下有用。
第二步:在 zulip.conf 中声明信任的代理
把下面的配置块追加到/etc/zulip/zulip.conf,ips一行填第一步确定的地址,多个地址用逗号分隔:
[loadbalancer] # Use the IP addresses you determined above, separated by commas. ips = 192.168.0.100ips = 192.168.0.100是文档示例值,替换为你自己的代理地址或 CIDR 网段。该配置项在 系统配置文档 中的定义是:逗号分隔的外部负载均衡器 IP 或网段列表,来自这些地址的X-Forwarded-For和X-Forwarded-Proto会被采信。
然后以 root 身份运行:
/home/zulip/deployments/current/scripts/zulip-puppet-apply该命令会重新生成 Zulip 的nginx配置,使其仅在接受来自上述代理 IP 时采信X-Forwarded-For头。它属于部署脚本,会影响服务器上的服务配置,请在维护窗口执行。
最后重启 Zulip:
/home/zulip/deployments/current/scripts/restart-server可选:让 Zulip 以 HTTP 模式对接代理
如果反向代理对客户端说 HTTPS、对 Zulip 说 HTTP(即 TLS 在代理层终结),需要让 Zulip 的内置nginx监听 HTTP。文档说明这在 Zulip 服务器不直接暴露公网时可以保持安全。步骤:
在
/etc/zulip/zulip.conf中追加:[application_server] http_only = true以 root 运行
/home/zulip/deployments/current/scripts/zulip-puppet-apply,把 Zulip 的nginx配置改为允许 HTTP。运行
/home/zulip/deployments/current/scripts/restart-server重启。
文档特别强调:开启http_only后,必须正确配置 Zulip 信任代理的X-Forwarded-Proto头(即本文的第二步),否则客户端可能出现无限重定向。
代理侧:nginx 配置示例
文档给出了一份可直接工作的 nginx 完整示例,假设 Zulip 服务器位于https://10.10.10.10:443。使用前先完成上面的"信任代理"配置,然后按文档建议:根nginx.conf参考 Zulip 服务器上自带的/etc/nginx/nginx.conf(否则例如不设置client_max_body_size就无法上传大文件),站点配置放在/etc/nginx/sites-available,以下示例作为起点:
server { listen 80; listen [::]:80; location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name zulip.example.com; ssl_certificate /etc/letsencrypt/live/zulip.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/zulip.example.com/privkey.pem; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $host; proxy_http_version 1.1; proxy_buffering off; proxy_read_timeout 20m; proxy_pass https://10.10.10.10:443; } }其中server_name、ssl_certificate、ssl_certificate_key、proxy_pass是文档示例值,需替换为你的域名、证书路径和 Zulip 服务器地址。proxy_set_header X-Forwarded-Proto $scheme;这一行是关键:它按浏览器与代理之间的实际协议设置转发头。
使用 Apache2 或 HAProxy 作为代理时,文档同样提供了完整配置示例(Apache2 需要a2enmod ssl proxy proxy_http headers rewrite并设置USE_X_FORWARDED_HOST = True;HAProxy 的 frontend/backend 中用http-request set-header设置X-Forwarded-Proto),见 reverse-proxies.md 的 Apache2 与 HAProxy 章节。
其他代理实现需要满足的通用要求
如果你用的不是文档示例中的 nginx/Apache2/HAProxy,文档列出了几条必须满足的要求:
正确维护
X-Forwarded-For:该头应包含请求经过的各级 IP 地址序列,并且 Zulip 侧已按上文信任了这些代理地址。正确设置
X-Forwarded-Proto:值为浏览器与代理之间连接的协议(https或http),Django 会用它做 CSRF 检查。代理必须覆盖已有值设置该头,而不是追加一个新头。如果你的代理无法设置X-Forwarded-Proto,可以改为在负载均衡层做全部 HTTP 到 HTTPS 的重定向,并设置zulip.conf中的loadbalancer.rejects_http_requests;文档提醒该设置是安全隐患,除非负载均衡器单方面拒绝未加密的 HTTP 连接或对它们返回 301——且负载均衡器不得把任何未加密到达的请求转发给 Zulip,Zulip 的 HSTS 头不足以保护(API 客户端不遵循 HSTS)。透传客户端的
Host:头,而不是代理看到的内部主机名。如果做不到,可以在/etc/zulip/settings.py设置USE_X_FORWARDED_HOST = True,并把客户端的Host头放在X-Forwarded-Host中传给 Zulip。不要干扰 long-polling:Zulip 依赖 long-polling 做服务端实时推送。文档指出的两个关键
nginx选项:proxy_read_timeout 1200;(文档示例配置中写为20m)——必须显著高于 60 秒,对 events API 尤其重要,且要应用到所有端点;proxy_buffering off——否则代理可能对使用 events API 的客户端偶发返回 502。
仓库中对应片段可参考 proxy_longpolling。
避免 IPv4/IPv6 之间意外负载均衡:
nginx代理如果在同一主机名的 IPv4 和 IPv6 地址间负载均衡,会产生难以排查的怪异错误。确保upstream指向的地址不会意外解析出多个 IP(例如未配置多 IP 的 DNS 名称;IPv6 配置中偶尔会遇到这种情况)。
验证配置是否生效
文档给出的验证方式是检查日志:查看/var/log/zulip/server.log,确认其中记录的是客户端的真实 IP 地址,而不是代理服务器的 IP。如果日志里全是代理地址,说明X-Forwarded-For链路有问题,回头核对:代理是否设置了该头、[loadbalancer] ips是否覆盖了代理在 Zulip 主机视角下的实际地址、zulip-puppet-apply和restart-server是否都已执行。
如果出现无限重定向,通常是X-Forwarded-Proto没有配置好,或代理没有覆盖已有的头值;如果客户端偶发 502,先检查proxy_buffering off和长超时是否生效。
限制与下一步
- 以上路径针对标准安装脚本部署;Docker 部署的等价机制是
LOADBALANCER_IPS/TRUST_GATEWAY_IP,见 Docker 文档。 - 修改
[loadbalancer]、[application_server]等配置后,统一通过zulip-puppet-apply+restart-server生效,不要手工编辑生成的nginx配置文件。 - 部署与生产部署的其他选项(独立服务机器、备用端口等)见 deployment.md。
【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考