1) 开启 force_https(Discourse)
让站点一律走 HTTPS,并给 Cookie 加上
Secure,配合 NPM 传入的协议头使用。
./launcher enter app
rails r "SiteSetting.force_https = true"
exit
备注:这与 CSP 不同;
force_https负责重定向,CSP 负责前端资源白名单。
2) 启用 HSTS(NPM)与 CSP(Discourse)
-
HSTS:在 NPM → Proxy Host → SSL 勾选
Force SSL / HTTP/2 / HSTS / OCSP Stapling(HSTS 会在响应头里体现为strict-transport-security)。 -
CSP(内容安全策略):建议开启(如果之前为排错临时关闭过):
./launcher enter app
rails r "SiteSetting.content_security_policy = true"
exit
提醒:不要在 NPM 的 Advanced 里再手工添加 CSP 头,避免与 Discourse 自带的 CSP 冲突。
3) NPM → Proxy Host → Advanced(只保留必要请求头)
不要在这里写 301/302/重写规则,也不要加 CSP。
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
并在 Proxy Host 勾选:Websocket Support。
4) www → 非 www 用 Redirect Host 实现
让规范域名收敛到
abc.com,避免在 Proxy Host 里自写 301 造成双跳。
-
类型:重定向
-
域名:
www.abc.com -
转发域名:
abc.com -
Http代码:
301 Permanent(永久重定向,SEO 友好) -
保留路径:

-
阻止常见漏洞:

-
强制 SSL:

5)(强烈推荐)容器网络与目标主机名的规范
解决“偶发空白/样式错乱/循环”的根因之一是 Docker 内部 DNS 名称冲突。
-
Discourse 容器在
/var/discourse/containers/app.yml:expose: - "80" # 仅在容器网络内暴露,不做宿主端口映射 docker_args: - "--network=web"然后重建:
cd /var/discourse && ./launcher rebuild app -
NPM 的 docker-compose:把
services:的服务名不要叫app,避免与 Discourse 默认容器名冲突;例如:services: npm-proxy: container_name: npm ... networks: [web] networks: web: external: true -
NPM 的 Proxy Host → Forward Hostname 填:
discourse,端口80,Schemehttp(不要再填app或容器 IP)。
6) 验证(两端各测一次)
# 公网侧:应以 200 结束(中途不应在 http↔https 来回跳)
curl -I -L https://abc.com
# NPM 容器里直连上游:应返回 Discourse 头(如 x-discourse-route)
docker exec -it npm curl -I http://app
常见坑位对照表
-
在 Proxy Host 的 Advanced 里写了 301/重写 → 可能与 force_https / Redirect Host 叠加,触发循环。
-
在 NPM 高级 自定义Nginx配置里自加 CSP → 与 Discourse 的 CSP 冲突,导致前端资源被拦,页面空白或转圈。
-
Docker 内部 DNS 名称冲突(NPM 的 service 叫
app,Discourse 默认容器也叫app)→ 反代偶尔打到 NPM 自己。