「求助」关于 域名 + frp 内网穿透访问OpenWebUI无法对话的问题

前情提要:
由于长期通过 公网ip+端口 内网穿透访问家中电脑部署的openwebui.
加上买了个还不错的域名, 因此想配置一下, 用域名访问openwebui, 方便使用也可以提供给好友使用(毕竟我的ip他们也记不住). https看着也更舒服, 地址栏也不会有不安全的提示.

折腾的过程其实有些坎坷, 不过好在是完成了. 当我用域名成功打开openwebui并登陆成功后, 我还是挺开心的.
二话不说开个新对话, 找个模型打个招呼…

然后就寄了.. F12也看过, 不懂什么SSE 流格式. 跟OpenRouter的DeepSeekV3聊了很久(毕竟才充了10刀, 是时候发挥用处了), 不过目前还是没啥进展.

以下是我情况总结. 搞到这里我还是想完成最后一步实现目标的.
有空的佬过过目, 看下是否有理论或实践的经验分享一下, 希望佬们不吝赐教, 感谢 !

核心问题:

访问部署在内网并通过 FRP + Nginx 暴露到公网的 OpenWebUI 时,使用 域名 (https://ai.xxx.com) 访问会导致与模型对话失败,具体表现为前端收到 /api/chat/completions 接口返回的 SSE 流式数据(包含 : OPENROUTER PROCESSINGdata: {...} 行),并因此抛出 JSON 解析错误 (SyntaxError)。而使用 公网 IP + 端口 (http://公网IP:端口) 直接访问(或通过 FRP 的 TCP 端口转发访问)时,完全正常/api/chat/completions 接口返回的是前端预期的单个 JSON 对象(类似 {"status":true,"task_id":"..."})。

已确认和工作正常的部分:

  1. 内网穿透 (FRP): FRP 服务端 (frps) 和客户端 (frpc) 配置基本正确,能够成功建立隧道。无论是 tcp 类型代理还是 http/https 类型代理,都能将流量转发到内网的 OpenWebUI 服务。
  2. Web 服务器 (Nginx):
  • Nginx 成功监听 80 和 443 端口。
  • HTTPS 配置正确,使用了有效的 Let’s Encrypt 证书(已确认包含 ai.xxx.com)。
  • 能够将来自 ai.xxx.com 的请求反向代理到 FRP 监听的端口。
  1. 域名解析 & 网络访问: 域名 ai.xxx.com 正确解析到公网服务器 IP。通过域名可以成功加载 OpenWebUI 的界面、历史记录等静态资源和部分 API。
  2. OpenWebUI 服务本身: 在内网直接访问或通过公网 IP + 端口访问时,服务功能完整,对话正常。说明 OpenWebUI 核心服务和连接 OpenRouter API 的逻辑是通的。

关键的异常点:

  • /api/chat/completions 接口响应格式不一致: 这是问题的核心。同一个后端接口,根据访问路径(域名 vs IP)的不同,返回了截然不同的数据格式。
  • 前端解析失败: 由于域名访问时收到了非预期的 SSE 流,前端 JS 代码尝试将其作为 JSON 解析,导致 SyntaxError,对话流程中断。

当前最可能的根源推测:

  1. OpenWebUI 后端行为差异(概率最大): OpenWebUI 的后端逻辑在处理 /api/chat/completions 请求时,很可能根据收到的 HTTP 请求头 (特别是 Host Header,但也可能是 X-Forwarded-Proto, X-Forwarded-For, Accept 等) 判断应该返回 SSE 流还是 JSON。当请求来自 ai.xxx.com (经过 Nginx 转发) 时,这些头信息与直接 IP 访问时不同,触发了返回 SSE 流的逻辑分支。
  2. Nginx 配置影响: 虽然 Nginx 成功转发了请求,但它在转发过程中修改或添加的某些 Header(即使是标准 Header 如 X-Forwarded-Proto 或默认行为)可能无意中影响了 OpenWebUI 后端的判断。

下一步最有效的排查方向:

  1. 深入检查 OpenWebUI 配置和逻辑:
  • 查找 OpenWebUI 的配置文件、环境变量或启动参数中,任何与 流式输出 (stream/sse)、域名、代理、主机名 (host) 相关的设置。
  • 如果可能,查看处理 /api/chat/completions 的后端源代码,确认是否存在基于 request.headers 的条件判断来决定响应格式。
  1. 详细对比请求头: 在 OpenWebUI 后端添加日志,记录下通过域名访问和通过 IP 访问时,/api/chat/completions 收到的 完整 HTTP 请求头,进行逐一对比,找出关键差异。
  2. 尝试在 Nginx 中操纵 Header: 作为实验,尝试在 Nginx 的 proxy_set_header 指令中,强制设置 Accept: application/json 或其他可能影响响应格式的头,看是否能改变 OpenWebUI 的行为。

目前,问题已经缩小到“为什么 OpenWebUI 对于看似相同(但实际 Header 不同)的请求,返回了两种不同的响应格式”。解开这个谜团的关键在于理解 OpenWebUI 内部的处理逻辑。

3 Likes

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;

有的, 配置的问题, 在询问大模型的时候, 反复提过好几次, 我也试过.

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访问端口(需证书)

看下控制台和网络有没有报错

openwebui先设置环境变量ENABLE_WEBSOCKET_SUPPORT为false试试看,排除websocket的影响

把fprc的配置模糊掉关键信息后贴一下,用的https2http还是什么?

确实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”]

frp 0.50.0版本始自带自签证书的tls加密,无需使用https。
nginx配置文件改成下面试试:

location / {
    # 修改这行 proxy_pass http://127.0.0.1:8443; 
    proxy_pass http://127.0.0.1:8080; 
    ...

原来还可以这样,学到了,我是用1panel反代的,感觉你这样比较优雅。

不如直接上2核4G服务器+域名最稳,内网穿透延迟比较高

是的, 而且有一个好处, 不用担心网络问题, 毕竟请求是从部署openWebUI的电脑发出的

确实是简单直接的办法了. 不过我不想折腾备案, 所以没用阿里云的服务器, 用了之前买的一个低配置的vps, 就是寻思转发一下, 凑合用. 其实也没太高延迟, 也能直连.
不过佬有没价位比较OK的推荐服务器推荐, 我搞一个来部署这些. 需要ip能直通那种~

是的,之前本地部署过,延迟感人遂放弃。目前倾向于AWS+CF