2026-10-14 09:44:28 +08:00
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# all-kefu 生产反代模板(openresty / nginx 通用,完整可抄)
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# 拓扑(spec §9):所有域名(租户域名 × N + 管理后台)反代到同一个容器
|
|
|
|
|
|
# chat.agent.hnxinyouhe.com (租户 auto) ─┐
|
|
|
|
|
|
# chat2.agent.hnxinyouhe.com (租户 jiancai) ─┼→ 127.0.0.1:8018 → all-kefu 容器
|
2026-10-19 19:44:46 +08:00
|
|
|
|
# <admin 域名>(形态 A,部署前由需求方确定)─┤
|
|
|
|
|
|
# <共享域名>(路径模式 /t/<slug>/,见下方共享域名 server 块)─┘
|
2026-10-14 09:44:28 +08:00
|
|
|
|
#
|
|
|
|
|
|
# 两种用法:
|
|
|
|
|
|
# · 独立 openresty/nginx:整份可抄——upstream + server 块齐备,按需删掉
|
|
|
|
|
|
# 不用的块(admin 只用形态 A 或 B 其一)。
|
|
|
|
|
|
# · 1Panel:server 块由面板生成,**只抄 location 片段**到
|
|
|
|
|
|
# /opt/1panel/www/sites/<域名>/proxy/*.conf——文末「1Panel 适配注记」
|
|
|
|
|
|
# 有逐条对照与现成片段。
|
|
|
|
|
|
#
|
|
|
|
|
|
# 血泪纪律(每条都出过线上事故,改配置前重读一遍):
|
|
|
|
|
|
# 1. Host 头必须透传(proxy_set_header Host $host)——本项目按 Host 头
|
|
|
|
|
|
# 解析租户,漏了这一行全站 404(不是报错,是静默 404)。
|
|
|
|
|
|
# 2. WS 升级头(Upgrade/Connection)全站只能出现一套——1Panel 面板生成
|
|
|
|
|
|
# 的反代若已带一套,再抄一份会重复定义,uvicorn 严格解析直接拒绝
|
|
|
|
|
|
# 握手(HTTP 正常、WS 426)。
|
|
|
|
|
|
# 3. client_max_body_size 至少 5m——头像上传上限 2MB,nginx 默认 1m
|
|
|
|
|
|
# 会直接 413(应用层校验根本收不到请求)。
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
# ---- upstream(独立部署时用;1Panel 场景见文末注记,直接写 IP:端口) --------
|
|
|
|
|
|
upstream all_kefu {
|
|
|
|
|
|
server 127.0.0.1:8018; # 单容器单 upstream;换端口只改这里
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# ---- 租户域名 server 块(每个租户域名复制一份,改 server_name 即可) --------
|
|
|
|
|
|
# 含:全站反代 + WS 单套升级头 + /api/public(widget 头像/品牌,放行即全站
|
|
|
|
|
|
# 反代的一部分,单独点名是防"按路径收紧"时漏掉它——漏配的症状是 widget
|
|
|
|
|
|
# 头像 404)。
|
2026-10-19 19:44:46 +08:00
|
|
|
|
#
|
|
|
|
|
|
# XFF 注记(本模板所有 `X-Forwarded-For $proxy_add_x_forwarded_for` 行同款
|
|
|
|
|
|
# 语义,含 /ws/、admin 两形态与共享域名块——改任何一处前先读这里):
|
|
|
|
|
|
# 登录限流取 XFF **末段**(app/platform/routes/auth_routes.py _client_ip)
|
|
|
|
|
|
# ——依赖本指令的**追加**语义(客户端自带 XFF 在前、反代把真实来源
|
|
|
|
|
|
# $remote_addr 追加在尾)。**勿改成 `X-Forwarded-For $remote_addr` 覆盖式**
|
|
|
|
|
|
# ——多 CDN 场景语义漂移;单跳拓扑下两者等价,但保持追加式。同一行的
|
|
|
|
|
|
# 首段是引擎 IP/区域解析的消费位(取第一段),两个消费方共用这一行,
|
|
|
|
|
|
# 动它两头都伤。
|
2026-10-14 09:44:28 +08:00
|
|
|
|
server {
|
|
|
|
|
|
listen 80; # 1Panel/证书层负责 443 与 HTTPS;独立部署自行补 listen 443 ssl
|
|
|
|
|
|
server_name chat2.agent.hnxinyouhe.com; # ← 每个租户域名一份:chat.(auto)/ chat2.(jiancai)
|
|
|
|
|
|
|
|
|
|
|
|
# WebSocket(/ws/chat):升级头单套 + 长读超时(长连接心跳间隔内不断开)
|
|
|
|
|
|
location /ws/ {
|
|
|
|
|
|
proxy_pass http://all_kefu;
|
|
|
|
|
|
proxy_http_version 1.1;
|
|
|
|
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
|
|
|
|
proxy_set_header Connection "upgrade";
|
|
|
|
|
|
proxy_set_header Host $host; # 纪律 1:WS 也按 Host 解析租户
|
|
|
|
|
|
proxy_set_header X-Real-IP $remote_addr;
|
2026-10-19 19:44:46 +08:00
|
|
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 首段=引擎 IP/区域、末段=登录限流(注记见块首)
|
2026-10-14 09:44:28 +08:00
|
|
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
|
|
proxy_read_timeout 3600s;
|
|
|
|
|
|
proxy_send_timeout 3600s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 其余全部(/, /static, /api/chat*, /api/public, /health …)——
|
|
|
|
|
|
# /static/widget.js 的 no-cache 由应用层中间件下发(响应头已带
|
|
|
|
|
|
# Cache-Control: no-cache),反代无需另加,也别加会覆盖它的 expires。
|
|
|
|
|
|
location / {
|
|
|
|
|
|
proxy_pass http://all_kefu;
|
|
|
|
|
|
proxy_http_version 1.1;
|
|
|
|
|
|
proxy_set_header Host $host; # 纪律 1
|
|
|
|
|
|
proxy_set_header X-Real-IP $remote_addr;
|
2026-10-19 19:44:46 +08:00
|
|
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加式,勿改覆盖式(注记见块首)
|
2026-10-14 09:44:28 +08:00
|
|
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
|
|
proxy_read_timeout 300s;
|
|
|
|
|
|
client_max_body_size 5m; # 纪律 3(头像上传)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# ---- admin 形态 B(同域名路径分流;不用独立管理域名时启用) --------------
|
|
|
|
|
|
# 应用层对 /admin 与 /api/platform/ 前缀豁免租户 Host 解析,挂在租户
|
|
|
|
|
|
# 域名下照常工作(与租户聊天面共存,同源零 CORS)。启用 = 取消注释;
|
|
|
|
|
|
# nginx 最长前缀匹配,这两块优先于上面的 location /。
|
|
|
|
|
|
# 已知边界:形态 B 下后台"品牌-头像预览"走本域 /api/public/avatar,
|
|
|
|
|
|
# 解析到的是**本域名对应的租户**——切换到其他租户的配置页时头像预览
|
|
|
|
|
|
# 不准(已知项,CLAUDE.md Phase 4 清理单 #3/#4)。
|
|
|
|
|
|
#
|
|
|
|
|
|
# location /admin {
|
|
|
|
|
|
# proxy_pass http://all_kefu;
|
|
|
|
|
|
# proxy_http_version 1.1;
|
|
|
|
|
|
# proxy_set_header Host $host;
|
|
|
|
|
|
# proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
|
|
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
|
|
|
|
# proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
|
|
# proxy_read_timeout 300s;
|
|
|
|
|
|
# client_max_body_size 5m; # 头像上传走 /api/platform/.../brand/avatar
|
|
|
|
|
|
# }
|
|
|
|
|
|
# location /api/platform/ {
|
|
|
|
|
|
# proxy_pass http://all_kefu;
|
|
|
|
|
|
# proxy_http_version 1.1;
|
|
|
|
|
|
# proxy_set_header Host $host;
|
|
|
|
|
|
# proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
|
|
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
|
|
|
|
# proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
|
|
# proxy_read_timeout 300s;
|
|
|
|
|
|
# client_max_body_size 5m;
|
|
|
|
|
|
# }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-19 19:44:46 +08:00
|
|
|
|
# ---- 共享域名 server 块(路径模式 /t/<slug>/,注释示例——启用 = 取消注释) ----
|
|
|
|
|
|
# 单域名承载任意多租户(spec 2026-10-19,部署手册 §3.6):租户标识在 URL
|
|
|
|
|
|
# 路径 /t/<slug>/ 中,与上方"每租户一域名"的 Host 模式共存(优先级:
|
|
|
|
|
|
# 路径前缀 > Host)。接入零 DNS:平台设置配 shared_widget_domain → 建租户
|
|
|
|
|
|
# (域名可留空)→ 后台复制"共享域名形态"嵌入代码贴到目标网站。地址形态:
|
|
|
|
|
|
# 聊天页 https://<共享域名>/t/<slug>/ 挂件 .../t/<slug>/static/widget.js
|
|
|
|
|
|
# 已知边界(照抄前先读):
|
|
|
|
|
|
# 1. 路径模式 WS 的真实路径是 /t/<slug>/ws/chat(widget.js 按 basePath
|
|
|
|
|
|
# 拼接),不带 /t 前缀的 `location /ws/` 匹配不到它——升级头用下面
|
|
|
|
|
|
# 的 regex location 承载(nginx regex location 优先于前缀 location /,
|
|
|
|
|
|
# 正好圈中"路径模式 + /ws/"的交集;1Panel 同样放 proxy/ws.conf)。
|
|
|
|
|
|
# 2. 路径前缀模式要求应用以**域名根**部署——uvicorn 不得加 `--root-path`
|
|
|
|
|
|
# (root_path 会前移 raw_path,/t/ 前缀解析永不命中,全站退化为 Host
|
|
|
|
|
|
# 解析 404)。
|
|
|
|
|
|
# 3. `/t/<slug>`(无尾斜杠)→ 404 属预期(仅带尾斜杠形态剥离前缀);
|
|
|
|
|
|
# 嵌入代码生成的就是带斜杠正确形态,勿手工"修正"。
|
|
|
|
|
|
#server {
|
|
|
|
|
|
# listen 80;
|
|
|
|
|
|
# server_name kefu.example.com; # ← 占位:共享域名部署前由需求方确定
|
|
|
|
|
|
#
|
|
|
|
|
|
# # WebSocket(路径模式 /t/<slug>/ws/chat):升级头单套 + 长读超时
|
|
|
|
|
|
# location ~ ^/t/[a-z0-9-]+/ws/ {
|
|
|
|
|
|
# proxy_pass http://all_kefu;
|
|
|
|
|
|
# proxy_http_version 1.1;
|
|
|
|
|
|
# proxy_set_header Upgrade $http_upgrade;
|
|
|
|
|
|
# proxy_set_header Connection "upgrade";
|
|
|
|
|
|
# proxy_set_header Host $host; # 纪律 1:路径模式按 /t/<slug> 解析租户,
|
|
|
|
|
|
# # 但 Host 头仍须透传(豁免前缀按它工作)
|
|
|
|
|
|
# proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
|
|
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加式,勿改覆盖式(注记见租户域名块)
|
|
|
|
|
|
# proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
|
|
# proxy_read_timeout 3600s;
|
|
|
|
|
|
# proxy_send_timeout 3600s;
|
|
|
|
|
|
# }
|
|
|
|
|
|
#
|
|
|
|
|
|
# # 其余全部(/t/<slug>/ 聊天页、/t/<slug>/static/*、/t/<slug>/api/*、
|
|
|
|
|
|
# # /health …)——同租户域名块:no-cache 由应用层下发,反代别加 expires。
|
|
|
|
|
|
# location / {
|
|
|
|
|
|
# proxy_pass http://all_kefu;
|
|
|
|
|
|
# proxy_http_version 1.1;
|
|
|
|
|
|
# proxy_set_header Host $host; # 纪律 1
|
|
|
|
|
|
# proxy_set_header X-Real-IP $remote_addr;
|
|
|
|
|
|
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加式,勿改覆盖式(注记见租户域名块)
|
|
|
|
|
|
# proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
|
|
# proxy_read_timeout 300s;
|
|
|
|
|
|
# client_max_body_size 5m; # 纪律 3
|
|
|
|
|
|
# }
|
|
|
|
|
|
#}
|
|
|
|
|
|
|
2026-10-14 09:44:28 +08:00
|
|
|
|
# ---- admin 形态 A:独立管理域名 server 块(与形态 B 二选一) ------------------
|
|
|
|
|
|
# 整域反代到同一容器:/admin(SPA 静态,含 assets)与 /api/platform(后台
|
|
|
|
|
|
# API,认证由路由依赖守护)同一 upstream。全站反代即可,无需按路径拆分。
|
|
|
|
|
|
# 入口是 https://<admin 域名>/admin/——管理域根路径 / 走租户 Host 解析,
|
|
|
|
|
|
# 404 属预期(要友好可在本块加 location = / { return 302 /admin/; })。
|
|
|
|
|
|
# 已知边界:独立管理域名下 /api/public/*(按租户 Host 解析)必 404——后台
|
|
|
|
|
|
# "品牌-头像预览"在该形态加载不出(预览以本地简化预览为准),已知项
|
|
|
|
|
|
# (CLAUDE.md Phase 4 清理单 #3),非配置错误,勿按 404 排障反代。
|
|
|
|
|
|
server {
|
|
|
|
|
|
listen 80;
|
|
|
|
|
|
server_name admin.example.com; # ← 占位:管理域名部署前由需求方确定
|
|
|
|
|
|
|
|
|
|
|
|
location / {
|
|
|
|
|
|
proxy_pass http://all_kefu;
|
|
|
|
|
|
proxy_http_version 1.1;
|
|
|
|
|
|
proxy_set_header Host $host; # 形态 A 的 Host 是管理域(未知租户)——
|
|
|
|
|
|
# /admin 与 /api/platform 豁免租户解析,照常工作
|
|
|
|
|
|
proxy_set_header X-Real-IP $remote_addr;
|
2026-10-19 19:44:46 +08:00
|
|
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加式,勿改覆盖式(注记见租户域名块)
|
2026-10-14 09:44:28 +08:00
|
|
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
|
|
|
|
proxy_read_timeout 300s;
|
|
|
|
|
|
client_max_body_size 5m; # 头像上传
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# 1Panel 适配注记(jc 项目两次线上事故的源头,逐条核对)
|
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
# 1. server 块由面板建站生成,不要手写整个 server——只把上面的 location
|
|
|
|
|
|
# 片段放进站点目录:
|
|
|
|
|
|
# /opt/1panel/www/sites/<域名>/proxy/root.conf ← location /(全站反代)
|
2026-10-19 19:44:46 +08:00
|
|
|
|
# /opt/1panel/www/sites/<域名>/proxy/ws.conf ← location /ws/(升级头单套;
|
|
|
|
|
|
# 共享域名站为 regex 形态
|
|
|
|
|
|
# `~ ^/t/[a-z0-9-]+/ws/`,
|
|
|
|
|
|
# 见上方共享域名块)
|
2026-10-14 09:44:28 +08:00
|
|
|
|
# /opt/1panel/www/sites/<域名>/proxy/admin.conf ← 形态 B 的两块(启用时)
|
|
|
|
|
|
# 片段内 proxy_pass 直接写 http://127.0.0.1:8018——upstream 块只能声明在
|
|
|
|
|
|
# http 上下文,server 级 include 文件里放不进去(面板世界没有"全局
|
|
|
|
|
|
# upstream 文件"的挂载点,直写地址即单一事实源,换端口全文替换)。
|
|
|
|
|
|
#
|
|
|
|
|
|
# 2. 主 conf 的 include 行检查(1Panel 坑:面板建站默认可能不带):
|
|
|
|
|
|
# /opt/1panel/www/conf.d/<域名>.conf
|
|
|
|
|
|
# 里必须有(没有就手工补,改前备份同目录 *.conf.bak.<日期>):
|
|
|
|
|
|
# include /www/sites/<域名>/proxy/*.conf;
|
|
|
|
|
|
# 注意路径前缀是 /www/sites/...(1Panel 容器内视角),不是 /opt/1panel/...。
|
|
|
|
|
|
# 面板配了反代却不生效,九成是这行缺失。
|
|
|
|
|
|
#
|
|
|
|
|
|
# 3. 1Panel 面板生成的反代若自带 Upgrade/Connection 头,删掉面板那套再放
|
|
|
|
|
|
# ws.conf(升级头只能一套,见顶部纪律 2)。
|
|
|
|
|
|
#
|
|
|
|
|
|
# 4. 改完必验再重载(先 -t 再 reload;reload 平滑,不影响其他站点):
|
|
|
|
|
|
# docker exec $(docker ps --filter name=openresty -q | head -1) nginx -t
|
|
|
|
|
|
# docker exec $(docker ps --filter name=openresty -q | head -1) nginx -s reload
|
|
|
|
|
|
# 验证(逐域名):
|
|
|
|
|
|
# curl -s https://<租户域名>/health | head -c 200 # {"status":"ok",...}
|
|
|
|
|
|
# curl -sI https://<租户域名>/static/widget.js | grep -i cache # Cache-Control: no-cache
|
|
|
|
|
|
# curl -s -i --http1.1 -H "Connection: Upgrade" -H "Upgrade: websocket" \
|
|
|
|
|
|
# -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" -H "Sec-WebSocket-Version: 13" \
|
2026-10-14 09:52:23 +08:00
|
|
|
|
# https://<租户域名>/ws/chat | head -1 # 期待 HTTP/1.1 101
|
2026-10-14 09:44:28 +08:00
|
|
|
|
# =============================================================================
|