首先看成果

🚀 场景:棘手的问题
在一台离线服务器上,通过 Docker Compose 成功部署了 Dify。一切看起来都很完美:
.env文件里的APP_WEB_URL和SERVICE_API_URL都已配置为正确的公网 IP (例如http://172.20.**.***:15001)。- 外层的 Nginx 反向代理也已正确配置。
- Dify 的后台管理界面 (
/app/...) 访问完全正常。
但是,当你满心欢喜地打开“应用”里的“公开访问 URL” (/chat/...) 时,页面空白,F12 开发者工具显示一个刺眼的红色错误:
(failed) net::ERR_CONNECTION_REFUSED
Request URL: http://127.0.0.1:5001/api/webapp/access-mode?appCode=...
无论你怎么修改 .env 文件、硬编码 docker-compose.yml 里的环境变量、重启服务,这个 127.0.0.1 就像幽灵一样挥之不去。
🔍 根本原因:构建时 vs 运行时
在经历了漫长的排错后,我们终于锁定了根本原因。
Dify 的 web 容器是一个 Next.js 前端应用。对于 Next.js 来说,环境变量分为两种:
- 服务端变量:在服务器运行时读取(比如
APP_API_URL)。 - 客户端变量 (以
NEXT_PUBLIC_开头):这些变量在构建镜像时 (Build Time) 会被读取,并**硬编码(烘焙)**到最终生成的静态 JavaScript 文件中。
你从 Docker Hub pull 下来的官方 langgenius/dify-web:latest 镜像是 Dify 官方在他们服务器上提前构建好的。在构建时,它不知道你的 IP,所以它烘焙到 JS 文件里的 API 地址就是默认的 127.0.0.1。
这就是为什么你无论怎么在运行时 (Run Time) 传入环境变量,都无法改变 JS 文件里那个被写死的 127.0.0.1。
我们的思路是:
- 启动这个“坏的”
web容器。 docker exec进去。- 找到那个被烘焙了
127.0.0.1的 JS 文件。 - 用
sed命令强行替换 IP。 - 用
docker commit将修改后的容器保存为一个新的、已修复的本地镜像。 - 修改
docker-compose.yml使用这个新镜像。
详细操作步骤
第 1 步:进入正在运行的 web 容器
首先,确保你的 Dify 正在运行 (docker-compose up -d)。
Bash
# 找到你的 web 容器名
docker ps
# 假设容器名叫 dify-web-1
docker exec -it dify-web-1 /bin/sh
第 2 步:(在容器内) 搜索“病灶”文件
我们需要找到是哪个 JS 文件被硬编码了。
Bash
# (在容器的 sh 命令行里执行)
grep -r "http://127.0.0.1:5001" /app/web/.next/static/chunks/
在我的案例中,grep 返回了一个目标文件(你的文件名可能不同):
/app/web/.next/static/chunks/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page-cc67ab8adafd2566.js: ... "http://127.0.0.1:5001" ...
第 3 步:(在容器内) 执行 sed 替换手术
我们使用 sed 命令,将这个文件里的错误 IP 替换为我们的正确 IP。
注意:
- 将下面的
http://172.20.**.***:15001换成你自己的公网 API 地址。- 将下面文件名替换为你上一步搜到的文件名。
- 文件名中的
(、)、[、]需要用\来转义。
Bash
# (在容器内执行 - 这是一整行命令)
sed -i 's|http://127.0.0.1:5001|http://172.20.**.***:15001|g' /app/web/.next/static/chunks/app/\(commonLayout\)/app/\(appDetailLayout\)/\[appId\]/develop/page-cc67ab8adafd2566.js
执行完毕后,没有任何输出就是最好的结果。然后输入 exit 退出容器。
第 4 步:(在宿主机) 固化修改
我们已经修改了容器的文件系统,现在把它保存为一个新的本地镜像,命名为 dify-web-patched:latest。
Bash
# (在你的服务器上执行)
# 将 dify-web-1 替换为你的真实容器名
docker commit dify-web-1 dify-web-patched:latest
第 5 步:修改 docker-compose.yml
这是最后一步。编辑你的 docker-compose.yml,告诉 web 服务使用我们“打过补丁”的新镜像。
同时,我们必须保留 environment: 块,因为这些变量(如 APP_API_URL)对于 Next.js 的服务端渲染和 API 响应生成仍然至关重要。
YAML
services:
web:
# image: langgenius/dify-web:latest <--- 注释掉或删除这行
image: dify-web-patched:latest # <--- 替换为这行
# --- !! 关键:这个 environment 块必须保留 !! ---
environment:
CONSOLE_API_URL: "http://172.20.**.***:15001"
CONSOLE_WEB_URL: "http://172.20.**.***:17777"
APP_WEB_URL: "http://172.20.**.***:17777"
APP_API_URL: "http://172.20.**.***:15001"
SERVICE_API_URL: "http://172.20.**.***:15001"
# --- !! 保留结束 !! ---
depends_on: [ "api" ]
env_file: [ ".env" ]
volumes:
# ... 你的 volumes 保持不变 ...
restart: always
networks:
- dify_net
# ... 其他服务保持不变 ...
第 6 步:重启并见证奇迹
保存 docker-compose.yml,然后重启。
Bash
docker-compose down
docker-compose up -d
现在,清除你的浏览器缓存(或使用 Ctrl + Shift + R 强制刷新),再次打开那个“公开访问 URL”。
你会发现 127.0.0.1 的请求消失了,取而代之的是你正确的 172.20.**.***:15001 地址。离线部署的 Dify 完美运行!
6454

被折叠的 条评论
为什么被折叠?



