sinlee
(sinlee)
1
前情提要:
由于长期通过 公网ip+端口 内网穿透访问家中电脑部署的openwebui.
加上买了个还不错的域名, 因此想配置一下, 用域名访问openwebui, 方便使用也可以提供给好友使用(毕竟我的ip他们也记不住). https看着也更舒服, 地址栏也不会有不安全的提示.
折腾的过程其实有些坎坷, 不过好在是完成了. 当我用域名成功打开openwebui并登陆成功后, 我还是挺开心的.
二话不说开个新对话, 找个模型打个招呼…
然后就寄了.. F12也看过, 不懂什么SSE 流格式. 跟OpenRouter的DeepSeekV3聊了很久(毕竟才充了10刀, 是时候发挥用处了), 不过目前还是没啥进展.
以下是我情况总结. 搞到这里我还是想完成最后一步实现目标的.
有空的佬过过目, 看下是否有理论或实践的经验分享一下, 希望佬们不吝赐教, 感谢 !
核心问题:
访问部署在内网并通过 FRP + Nginx 暴露到公网的 OpenWebUI 时,使用 域名 (https://ai.xxx.com
) 访问会导致与模型对话失败,具体表现为前端收到 /api/chat/completions
接口返回的 SSE 流式数据(包含 : OPENROUTER PROCESSING
和 data: {...}
行),并因此抛出 JSON 解析错误 (SyntaxError
)。而使用 公网 IP + 端口 (http://公网IP:端口
) 直接访问(或通过 FRP 的 TCP 端口转发访问)时,完全正常,/api/chat/completions
接口返回的是前端预期的单个 JSON 对象(类似 {"status":true,"task_id":"..."}
)。
已确认和工作正常的部分:
- 内网穿透 (FRP): FRP 服务端 (frps) 和客户端 (frpc) 配置基本正确,能够成功建立隧道。无论是
tcp
类型代理还是 http
/https
类型代理,都能将流量转发到内网的 OpenWebUI 服务。
- Web 服务器 (Nginx):
- Nginx 成功监听 80 和 443 端口。
- HTTPS 配置正确,使用了有效的 Let’s Encrypt 证书(已确认包含
ai.xxx.com
)。
- 能够将来自
ai.xxx.com
的请求反向代理到 FRP 监听的端口。
- 域名解析 & 网络访问: 域名
ai.xxx.com
正确解析到公网服务器 IP。通过域名可以成功加载 OpenWebUI 的界面、历史记录等静态资源和部分 API。
- OpenWebUI 服务本身: 在内网直接访问或通过公网 IP + 端口访问时,服务功能完整,对话正常。说明 OpenWebUI 核心服务和连接 OpenRouter API 的逻辑是通的。
关键的异常点:
/api/chat/completions
接口响应格式不一致: 这是问题的核心。同一个后端接口,根据访问路径(域名 vs IP)的不同,返回了截然不同的数据格式。
- 前端解析失败: 由于域名访问时收到了非预期的 SSE 流,前端 JS 代码尝试将其作为 JSON 解析,导致
SyntaxError
,对话流程中断。
当前最可能的根源推测:
- OpenWebUI 后端行为差异(概率最大): OpenWebUI 的后端逻辑在处理
/api/chat/completions
请求时,很可能根据收到的 HTTP 请求头 (特别是 Host
Header,但也可能是 X-Forwarded-Proto
, X-Forwarded-For
, Accept
等) 判断应该返回 SSE 流还是 JSON。当请求来自 ai.xxx.com
(经过 Nginx 转发) 时,这些头信息与直接 IP 访问时不同,触发了返回 SSE 流的逻辑分支。
- Nginx 配置影响: 虽然 Nginx 成功转发了请求,但它在转发过程中修改或添加的某些 Header(即使是标准 Header 如
X-Forwarded-Proto
或默认行为)可能无意中影响了 OpenWebUI 后端的判断。
下一步最有效的排查方向:
- 深入检查 OpenWebUI 配置和逻辑:
- 查找 OpenWebUI 的配置文件、环境变量或启动参数中,任何与 流式输出 (stream/sse)、域名、代理、主机名 (host) 相关的设置。
- 如果可能,查看处理
/api/chat/completions
的后端源代码,确认是否存在基于 request.headers
的条件判断来决定响应格式。
- 详细对比请求头: 在 OpenWebUI 后端添加日志,记录下通过域名访问和通过 IP 访问时,
/api/chat/completions
收到的 完整 HTTP 请求头,进行逐一对比,找出关键差异。
- 尝试在 Nginx 中操纵 Header: 作为实验,尝试在 Nginx 的
proxy_set_header
指令中,强制设置 Accept: application/json
或其他可能影响响应格式的头,看是否能改变 OpenWebUI 的行为。
目前,问题已经缩小到“为什么 OpenWebUI 对于看似相同(但实际 Header 不同)的请求,返回了两种不同的响应格式”。解开这个谜团的关键在于理解 OpenWebUI 内部的处理逻辑。
3 Likes
lsolol
(韩老魔)
2
nginx要处理下websocket的支持,类似下面
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
sinlee
(sinlee)
3
有的, 配置的问题, 在询问大模型的时候, 反复提过好几次, 我也试过.
server {
listen 443 ssl;
server_name ai.xxx.com;
ssl_certificate /etc/letsencrypt/live/xxx.com/fullchain.pem; # Certbot 生成的证书路径
ssl_certificate_key /etc/letsencrypt/live/xxx.com/privkey.pem; # Certbot 生成的私钥路径
location / {
proxy_pass http://127.0.0.1:8443;
# 其他 proxy_set_header 指令保持不变,它们对于将正确的 Host 和协议信息传递给后端很重要
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;
# 如果 OpenWebUI 或其后端使用了 WebSocket,这些也需要保留
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
server {
listen 80;
server_name ai.xxx.com;
location / {
proxy_pass http://127.0.0.1:8080; # 指向FRP的vhostHTTPPort
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 关键添加 ↓↓↓
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
以及frps.toml配置:
bindPort = 7000
webServer.addr = “0.0.0.0”
webServer.port = 7500
webServer.user = “admin”
webServer.password = “admin”
添加HTTP/HTTPS代理监听
vhostHTTPPort = 8080 # 域名HTTP访问端口(如用80需root权限)
vhostHTTPSPort = 8443 # 域名HTTPS访问端口(需证书)
lsolol
(韩老魔)
5
openwebui先设置环境变量ENABLE_WEBSOCKET_SUPPORT为false试试看,排除websocket的影响
ongkz
(心意)
6
把fprc的配置模糊掉关键信息后贴一下,用的https2http还是什么?
sinlee
(sinlee)
7
确实frpc的配置也很关键, 是我遗漏了没贴出来, 感谢佬的提醒.
frpc.toml配置如下:
serverAddr = “8.8.8.8”
serverPort = 7000
[[proxies]]
name = “openWebUI”
type = “tcp”
localIP = “192.168.2.135”
localPort = 7095
remotePort = 7095
[[proxies]]
name = “openwebui-http”
type = “http”
localIP = “192.168.2.135”
localPort = 7095
customDomains = [“ai.example.com”]
[[proxies]]
name = “openwebui-https”
type = “https”
localIP = “192.168.2.135”
localPort = 7095
customDomains = [“ai.example.com”]
ongkz
(心意)
8
frp
0.50.0
版本始自带自签证书的tls加密,无需使用https。
nginx配置文件改成下面试试:
location / {
# 修改这行 proxy_pass http://127.0.0.1:8443;
proxy_pass http://127.0.0.1:8080;
...
Chris_Lao
(Chris Lao)
9
原来还可以这样,学到了,我是用1panel反代的,感觉你这样比较优雅。
不如直接上2核4G服务器+域名最稳,内网穿透延迟比较高
sinlee
(sinlee)
11
是的, 而且有一个好处, 不用担心网络问题, 毕竟请求是从部署openWebUI的电脑发出的
sinlee
(sinlee)
12
确实是简单直接的办法了. 不过我不想折腾备案, 所以没用阿里云的服务器, 用了之前买的一个低配置的vps, 就是寻思转发一下, 凑合用. 其实也没太高延迟, 也能直连.
不过佬有没价位比较OK的推荐服务器推荐, 我搞一个来部署这些. 需要ip能直通那种~
是的,之前本地部署过,延迟感人遂放弃。目前倾向于AWS+CF