Dify 启用 HTTPS 完整指南:SSL证书配置与Nginx反向代理实操 1. 为什么我建议你在 Dify 上尽早启用 HTTPS先说结论如果你正在用 Dify 搭建知识库问答、内部 AI 工具或者对外提供 AI 应用服务那么“配 SSL 证书 强制 HTTPS 访问”这件事应该排在功能开发之前完成。原因不复杂。Dify 这类平台一旦跑起来上面承载的基本都是真实的业务数据——用户的提问内容、上传的文档、流程编排的 API Key、甚至对接外部模型时的密钥信息。如果整个访问链路是明文 HTTP那这些数据在网络传输过程中等于裸奔。我自己见过不止一个团队功能都上线了才想起来检查证书配置结果发现管理后台和 API 接口全是 HTTP 直出数据在公网链路里随便抓包就能看到。另外一个现实问题现在很多浏览器对 HTTP 站点会直接打上“不安全”的标记用户在访问你的 AI 应用时第一印象就会打折扣。尤其是如果产品面向外部客户一个绿色的锁形图标远比一串“此站点不安全”的警告更能建立信任。这篇内容我会从证书选型、域名与端口规划、Nginx 反代配置、容器环境下的证书挂载、以及几种常见部署形态的排错思路这几个角度完整梳理一遍 Dify 平台配置 SSL 证书和 HTTPS 安全访问的实操路径。文章里用到的方案都是我实际部署验证过的不会只讲概念。顺便说一句如果你只是在内网环境自己开发调试HTTP 确实还能凑合但只要涉及任何跨网络访问、多人协作、或者对外发布HTTPS 就不是可选项而是必选项。2. 配置前的规划证书类型、域名和端口2.1 证书类型怎么选免费证书、云厂商证书还是自签名证书在动手之前先想清楚要用哪种证书这直接决定了后面的配置流程和信任程度。第一种是免费 DV 证书最典型的就是 Lets Encrypt 签发的证书以及国内各大云厂商提供的免费 SSL 证书。优点是零成本、自动续期方便、兼容性好缺点是有效期短Lets Encrypt 的证书只有 90 天必须配置自动续期。对于绝大多数 Dify 部署场景这种证书完全够用。第二种是云厂商的付费证书或者企业型 OV/EV 证书。适合对信任等级有硬性要求的企业环境比如金融机构、政务系统。好处是证书有效期内省心、信任级别高代价是需要花钱有些还要走人工审核流程。第三种是自签名证书只适合纯内网测试环境。它的特点是部署简单、完全免费但浏览器会报不受信任无法用于任何对外服务。我的建议是能不用就不用除非你完全控制客户端环境并且能手动导入证书信任链。从投入产出比来看个人开发者、中小团队、甚至一些初创公司直接用 Lets Encrypt 或者云厂商免费证书是最稳妥的路径。2.2 域名规划为什么我不建议用 IP 直连的方式配 HTTPS这是很多人容易忽略的点。证书签发的对象是域名不是 IP 地址。虽然也有 IP 证书这种产品但通用性和浏览器兼容性都不如域名证书。所以配置 HTTPS 前先准备一个域名解析到 Dify 服务器。比如你的业务域名是example.com那就让ai.example.com这个子域名指向 Dify 服务所在服务器的公网 IP。这样一来后续申请证书、配置 Nginx、做反向代理路径都会清晰很多。另外要考虑端口问题。Dify 默认的 Web 服务跑在 80 端口HTTP而 HTTPS 默认走 443 端口。如果你用 Docker Compose 方式部署通常的做法是让宿主机的 80/443 端口交给 Nginx 或者直接交给 Dify 自带的 Nginx 容器处理外面统一通过 443 端口访问。注意如果服务器上还跑了其他 Web 服务比如 Grafana、Jenkins提前规划好 443 端口归属避免后面出现端口冲突排查起来非常痛苦。2.3 部署架构的两种形态单机 Docker Compose 和 KubernetesDify 最常见的部署方式是 Docker Compose 单机部署这也是官方推荐的人门方式。它自带的nginx服务容器把前端、API、Web 服务都聚合到了一起。在这种形态下配置 SSL 证书的核心工作分成两步一是把证书文件放到 Nginx 容器能访问到的位置二是修改 Dify 自带的 Nginx 配置模板让 443 监听生效并指向证书。另一种形态是 Kubernetes 部署。这种情况一般通过 Ingress 来终结 TLS证书存放在 Secret 对象中Dify 应用本身不需要感知证书的存在。如果你已经上了 K8s直接用 cert-manager 自动签发证书会是最省力的方案。本文重点展开 Docker Compose 形态因为大部分读者应该都是这种部署方式。3. 核心实操用 Nginx 为 Dify 启用 HTTPS3.1 证书文件准备与存放路径规划假设你已经从 Lets Encrypt 或者云厂商那里拿到了证书你手上会得到两个关键文件证书文件通常是.pem或.crt后缀里面包含公钥和证书链信息私钥文件通常是.key后缀必须严格保密这两个文件需要放到服务器上并且要规划好路径。我一般习惯放在/opt/dify/certs/目录下mkdir -p /opt/dify/certs # 将证书和私钥上传到这个目录 # 例如 # /opt/dify/certs/example.com.pem # /opt/dify/certs/example.com.key务必检查一下私钥文件的权限。私钥暴露等于整个 HTTPS 防线崩溃chmod 600 /opt/dify/certs/example.com.key3.2 Docker Compose 环境中证书挂载的正确姿势Dify 官方提供的docker-compose.yaml里Nginx 容器默认只是把宿主机的 80 端口映射到了容器的 80 端口。要启用 HTTPS我们需要做两件事修改端口映射把宿主机的 443 端口也映射到容器的 443 端口把证书目录挂载进 Nginx 容器。修改docker-compose.yaml中 nginx 服务的 ports 部分services: nginx: ports: - 80:80 - 443:443 volumes: - ./certs:/etc/letsencrypt:ro这里我把宿主机的/opt/dify/certs目录映射到了容器的/etc/letsencrypt目录用只读模式挂载防止容器侧误改证书文件。实操心得有些教程喜欢把证书直接塞进 Nginx 镜像或者用 volume 挂载单文件。我个人的习惯是挂载整个目录而不是单文件这样以后证书续期、替换文件时不需要重启容器只要 Nginx reload 就能生效。3.3 Dify 自带 Nginx 配置模板的修改方法这是整个配置过程中最关键的步骤。Dify 的 Docker Compose 目录下有一个nginx子目录里面放着站点配置模板通常叫dify.conf或者类似的名字。我们要做的不是直接改容器内文件而是修改宿主机上的模板文件然后重启 Nginx 容器让它重新加载配置。进入目录确认文件位置cd /opt/dify/docker/nginx ls -la # 一般会看到类似 dify.conf 或 nginx.conf 的文件编辑配置文件时我建议保留原 HTTP 配置作为跳转而不是直接删掉 80 端口监听。这样的好处是用户访问旧链接时能自动跳转到 HTTPS不会因为协议变更导致访问失败。下面是一份经过验证的 Nginx 配置片段server { listen 80; server_name ai.example.com; # 将所有 HTTP 请求永久重定向到 HTTPS return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name ai.example.com; # 证书与私钥路径 ssl_certificate /etc/letsencrypt/example.com.pem; ssl_certificate_key /etc/letsencrypt/example.com.key; # 推荐的安全增强配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; client_max_body_size 15M; location / { proxy_pass http://web:3000; 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; } location /api { proxy_pass http://api:5001; 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; } }这里有一个核心逻辑需要理解Dify 容器内部用的是服务名互相通信Nginx 容器通过 Docker 内部网络把请求转发给web容器前端端口 3000和api容器后端 API端口 5001。我们只要保持这种内部转发关系不变只在外层加上 443 监听和证书配置就能无缝升级到 HTTPS。需要注意X-Forwarded-Proto这个请求头。Dify 的后端服务需要知道用户的原始请求是 HTTP 还是 HTTPS才能生成正确的回调地址、网页跳转链接和 API 响应。如果少了这个头Dify 内部可能会生成指向http://的链接导致页面能打开但部分功能异常。3.4 修改完成后重启容器并验证配置文件改好后回到 Dify 的 docker 目录执行cd /opt/dify/docker docker compose down docker compose up -d如果不想中断服务也可以只重启 Nginx 容器docker compose restart nginx然后检查容器是否正常运行docker compose ps接下来验证 HTTPS 是否生效curl -I https://ai.example.com # 如果返回 200 OK说明 HTTPS 已经打通 # 如果返回证书错误多半是证书路径或者证书链配置有问题用浏览器访问https://ai.example.com确认地址栏出现锁形图标并且没有红色警告。4. Nginx 与容器编排里的常见配置坑4.1 端口映射漏改导致 HTTPS 无法访问这是我见过最多的问题。很多人在 Nginx 容器里改了监听 443 的配置但docker-compose.yaml的端口映射部分却还是只有80:80。结果就是容器内 Nginx 明明在监听 443但宿主机上根本看不到 443 端口外部请求自然进不来。排查命令很直接netstat -tlnp | grep 443 # 或者 ss -tlnp | grep 443如果没有输出基本可以断定是端口映射没生效赶紧回docker-compose.yaml补上443:443。4.2 证书链不完整导致部分浏览器报错有时候curl访问没问题但浏览器报 SSL 错误原因多半是证书链没配全。很多免费证书包含两个文件域名证书和中间证书。部分厂商会帮你合成好一个.pem但有些需要手动拼接。拼接方法很简单直接把中间证书内容追加到域名证书内容后面即可cat example.com.crt intermediate.crt fullchain.pem然后 Nginx 里引用这个合成的fullchain.pem文件。实操心得用curl -vI https://your.domain可以查看完整的证书链信息。如果输出里只有一层证书缺少 intermediate 层级那就大概率需要手动拼接。4.3 容器内时间不同步导致的证书校验失败这个问题非常隐蔽。TLS 握手时客户端会校验服务端证书的有效期如果服务器系统时间或容器时间严重偏离当前时间证书会被判定为未生效或已过期。如果你用的是云服务器建议开启 NTP 自动同步时间。在宿主机上执行timedatectl set-ntp true同时检查容器时间docker exec dify-nginx-1 date如果容器时间和宿主机不一致可以在docker-compose.yaml中增加时间卷挂载volumes: - /etc/localtime:/etc/localtime:ro这种问题往往只影响 HTTPS不影响 HTTP所以很多人排查了很久都找不到原因。5. 访问链路与内部服务转发让 Dify 正确识别 HTTPS5.1 修改 Dify 环境变量避免生成 HTTP 链接有些时候证书配好了页面也能打开但 Dify 应用内部生成的某些链接仍然是 HTTP。症状包括分享出来的对话链接打开后跳回 HTTP、部分 API 回调地址异常等。这通常是因为 Dify 后端不知道外部访问协议已经变成了 HTTPS。需要在 Dify 的环境配置中显式声明。打开docker-compose.yaml找到api服务的 environment 部分确认或者添加以下变量environment: # 其他配置保持不变 CONSOLE_API_URL: https://ai.example.com/api SERVICE_API_URL: https://ai.example.com APP_WEB_URL: https://ai.example.com这三个变量分别控制控制台 API、服务 API 和应用 Web 页面的基础 URL。改成 HTTPS 后Dify 生成的所有链接都会基于这个地址拼接不会再出现协议不对的情况。改完环境变量后需要重启 api 和 web 容器docker compose up -d5.2 浏览器缓存和 Service Worker 导致的“顽固”问题如果你之前用 HTTP 访问过 Dify升级 HTTPS 后浏览器可能还缓存着旧的 Service Worker 脚本。这时候哪怕服务器完全正常浏览器仍然可能报错或者加载不到资源。解决办法是强制刷新或者清除该站点缓存Windows/LinuxCtrl Shift RMacCmd Shift R如果问题依旧在浏览器开发者工具里进入 Application 面板手动注销 Service Worker然后刷新页面。这个坑很冷门但遇到一次就很头疼。6. 多域名与多应用场景下的 HTTPS 配置6.1 用同一张证书覆盖多个子域名如果你的 Dify 实例同时绑定多个子域名比如ai.example.com和console.example.com建议直接申请一张覆盖泛域名的证书*.example.com然后在 Nginx 配置中为不同 server_name 分别配置即可。如果用的是单域名证书那也没问题多加几个 server 块每个块指向对应证书即可。6.2 API 对外服务的 HTTPS 配置Dify 的 API 端口默认是 5001但对外提供服务时建议通过 Nginx 统一走 443而不是直接暴露 5001 端口。原因很直接5001 端口上跑的是裸 HTTP一旦开放到公网等于把 API 数据明文暴露在网络上而且端口扫描器往往会对非标准端口格外敏感。正确做法是在 Nginx 上为 API 单独加一个 location 转发或者用子域名解析到 API。比如server { listen 443 ssl http2; server_name api.example.com; ssl_certificate /etc/letsencrypt/example.com.pem; ssl_certificate_key /etc/letsencrypt/example.com.key; location / { proxy_pass http://api:5001; 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; } }所有通过 SDK 或者 HTTP 客户端访问 Dify API 的请求统一走https://api.example.com既安全又整洁。6.3 同一台服务器上部署其他服务时的端口规划很多人的服务器上不只有 Dify可能还跑了 Compose 之外的其他容器或者应用。这时要特别注意 443 端口只能被一个进程监听。如果你的 Nginx 容器占了 443那其他服务可以用Nginx 反向代理的方式挂在同一个 443 端口下用不同域名区分而不是再开一个 443。比如# 域名 ai.example.com 指向 Dify server { listen 443 ssl http2; server_name ai.example.com; # Dify 反代配置省略... } # 域名 tools.example.com 指向另外一个工具 server { listen 443 ssl http2; server_name tools.example.com; location / { proxy_pass http://tool-container:8080; } }这样所有服务统一走 443 TLS既安全又省事还避免了端口冲突。7. 自动续期用 Lets Encrypt 省掉 90 天的烦恼7.1 手动续期是反模式必须自动化Lets Encrypt 的证书有效期只有 90 天意味着每三个月你就要换一次证书。如果你觉得“续期不就是手动跑一下命令”那你就等着某天早上收到用户报告“证书过期了”的惊吓吧。自动续期这件事提前做好能省掉无数后续烦恼。官方推荐的工具是certbot。在宿主机上安装 certbotapt update apt install certbot -y然后用 webroot 模式或者 DNS 插件申请证书。最省事的做法是直接用 DNS 插件因为这样不需要在 Nginx 里做临时验证路径。以 Cloudflare DNS 为例其他 DNS 厂商也有对应插件原理一样certbot certonly --manual --preferred-challenges dns \ -d *.example.com \ -d example.com按提示在 DNS 管理后台添加对应的 TXT 记录验证通过后证书会生成在/etc/letsencrypt/live/example.com/目录。7.2 续期后自动部署证书到容器目录证书续期后Dify 的 Nginx 容器需要拿到新证书才能生效。如果使用简单的目录挂载那只需要把新证书复制到宿主机挂载目录然后重载 Nginx 即可。可以写一个简单的脚本放在 crontab 里#!/bin/bash # /usr/local/bin/renew-dify-cert.sh # 1. 执行续期 certbot renew --quiet # 2. 将新证书复制到 Dify 挂载目录 cp /etc/letsencrypt/live/example.com/fullchain.pem /opt/dify/certs/example.com.pem cp /etc/letsencrypt/live/example.com/privkey.pem /opt/dify/certs/example.com.key # 3. 让 Nginx 重新加载配置 docker exec dify-nginx-1 nginx -s reload然后加入 crontab每月执行一次足够crontab -e # 每月 1 号凌晨 3 点执行 0 3 1 * * /usr/local/bin/renew-dify-cert.sh实操心得帮我省过很多事的做法是先把证书复制脚本写好再单独手动跑一遍确认无报错最后才挂 crontab。不要在没验证脚本的情况下直接挂定时任务——否则命中的概率不高一旦命中就是证书过期事故。8. 常见问题速查表症状可能原因排查/解决办法浏览器访问 443 无响应docker-compose 端口映射没改检查 ports 是否有443:443浏览器报证书不受信任自签名证书或者证书链缺失用受信任 CA 证书或者拼接证书链curl 正常但浏览器报错证书链不完整拼接中间证书到 fullchain页面能打开但部分链接是 httpDify 环境变量未更新协议修改 CONSOLE_API_URL、SERVICE_API_URL、APP_WEB_URL容器时间不对导致证书校验失败服务器/容器时间偏差开启 NTP挂载 localtime页面加载资源 404浏览器缓存了旧 Service Worker强制刷新注销 Service Worker更换证书后仍报旧证书错误Nginx 未 reload执行docker exec dify-nginx-1 nginx -s reload访问/api接口报 502API 容器未启动或代理目标错误检查docker compose ps确认 api 容器运行正常9. 我踩过的一些坑和最终建议配置过程中最容易被忽视的其实是“环境变量更新”这一步。很多教程只讲了怎么配 Nginx没提 Dify 后端如何感知 HTTPS。结果就是证书生效了浏览器锁也有了但应用内生成的链接还是 HTTP用户点分享出来的链接又回到明文访问。这个问题我在第一次部署时就踩过后来看到日志里回调地址全是http://才意识到要改 API 服务的环境变量。另一个让我印象深刻的教训是不要把证书文件直接塞进容器镜像。当时我图省事把证书写进了自定义 Nginx 镜像结果证书快到期时要去重新构建镜像、推送、再拉取整个过程耗费了不少时间。改成目录挂载之后续期只要复制文件加 reload 就完成省心太多。如果你是从零开始部署 Dify我的建议是先把 HTTP 跑通确认服务正常再动手配置 HTTPS。不要把两件事混在一起否则出了问题很难分清是 Dify 配置问题还是 Nginx 问题。最后再分享一个小技巧配置完成后可以定期用一个外部监控服务去检查你的 HTTPS 端点设置好证书到期告警。这样即使自动续期脚本出了意外你也能提前发现不会等到用户来投诉才后知后觉。安全这件事很多时候拼的不是配置多高级而是能不能做到“提前发现问题”。