docs/embed-subdomain.md
一句话:把聊天页面单独放到
embed.example.com,与主站app.example.com分开。大多数部署不需要这一步——主站和 embed 同域就能用。只有对安全隔离有明确要求时再考虑。
嵌入聊天时,实际涉及三类地址(可以相同,也可以不同):
| 角色 | 举例 | 干什么 |
|---|---|---|
| A. 业务站点(宿主) | https://shop.example.com | 你的商城 / 文档站;在这里粘贴 Widget 脚本或 iframe |
| B. Embed 页面源站 | https://app.example.com 或 https://embed.example.com | 提供 embed.html、weknora-widget.js;聊天 iframe 加载自这里 |
| C. WeKnora API | 通常与 B 同域,如 https://app.example.com/api | 后端接口 |
默认(推荐入门):B 和主站管理后台都在 https://app.example.com,A 可以是任意第三方域名。
shop.example.com app.example.com
┌─────────────────┐ ┌──────────────────────────┐
│ <script src= │ │ /weknora-widget.js │
│ app.../widget> │──────►│ /embed/:channelId │
│ │ │ /api/v1/embed/... │
│ (浮窗 iframe) │◄──────│ │
└─────────────────┘ └──────────────────────────┘
独立子域(进阶):把 B 拆到 https://embed.example.com,主站 https://app.example.com 只留管理后台。
shop.example.com embed.example.com app.example.com
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Widget 脚本 │──────►│ embed 静态页+API │ │ 管理后台 │
│ iframe 指向 embed│◄──────│ (无 index SPA) │ │ (可选分离) │
└─────────────────┘ └──────────────────┘ └─────────────┘
| 场景 | 是否需要 |
|---|---|
| 内测、PoC、单域 Docker 部署 | 不需要 |
主站 X-Frame-Options: SAMEORIGIN,但要把聊天嵌到第三方页面 | 不需要(被嵌的是 /embed/* 页面,不是管理后台 SPA) |
| 希望 embed 站点不携带主站登录 Cookie、缩小暴露面 | 可以考虑 |
| 希望 CDN / WAF 对 embed 流量单独限速、缓存 | 可以考虑 |
| 合规要求「对外嵌入」与「内部管理」必须不同源 | 需要 |
本地开发:http://localhost:5173 同域即可,Vite 已把 /embed/* 指到 embed.html,不必配子域。
管理端生成 iframe / Widget 代码时,需要知道 B 的地址。在 frontend/public/config.js(或 Docker 启动脚本生成的配置)里设置:
window.__RUNTIME_CONFIG__ = {
// embed 页面与 widget.js 的源站;留空则使用当前浏览器所在域名
EMBED_BASE_URL: 'https://embed.example.com',
MAX_FILE_SIZE_MB: 50,
};
构建期也可通过环境变量注入:VITE_EMBED_BASE_URL=https://embed.example.com(可选)。
frontend/nginx.conf 顶部有注释掉的示例。要点:
server_name embed.example.com/embed/* → embed.html,/weknora-widget.js,/assets/*/api/ 反代到 WeKnora 后端(与主站相同后端即可)index.html 管理 SPA(减少攻击面)主站 server 块可继续 X-Frame-Options: SAMEORIGIN,不影响第三方嵌 embed 子域的 iframe。
渠道里的「域名白名单」校验的是 API 请求的 Origin 头,不是「允许哪些网站粘贴脚本」的抽象概念。
实际要填:
https://app.example.comhttps://embed.example.comPOST .../exchange 时,需带与白名单一致的 Origin 头(见 embed-secure-mode.md)第三方商城 https://shop.example.com 通常不用进白名单(它只是加载脚本,不直接调 embed API)。
若你把 embed 反代到商城同域路径下(少见),才需要填商城域名。
当宿主站 A 与 embed 源站 B 不同域时,weknora-widget.js 会自动给内部 iframe 加上 sandbox(也可手动 data-sandbox="true")。
A 与 B 同域时保持默认即可,无需 data-sandbox。
EMBED_BASE_URL 与真实访问地址一致(含 https)/embed/<渠道ID> 和 /weknora-widget.js/api/ 能连到后端