跳到主要内容

使用 Nginx 的 HTTPS

使用 Nginx 的 HTTPS

确保用户与 Open WebUI 之间的通信安全至关重要。HTTPS(HyperText Transfer Protocol Secure)会对传输数据进行加密,防止窃听和篡改。通过将 Nginx 配置为反向代理,你可以轻松为 Open WebUI 部署添加 HTTPS,同时提升安全性和可信度。

本指南提供三种设置 HTTPS 的方式:

  • 自签名证书:适合开发和内部使用,使用 Docker。
  • Let's Encrypt:适合需要受信任 SSL 证书的生产环境,使用 Docker。
  • Windows + 自签名证书:面向 Windows 上的开发和内部使用的简化说明,无需 Docker。
关键:为 WebSocket 连接配置 CORS

WebSocket 连接中一个非常常见、而且很难排查的问题,是跨源资源共享(CORS)策略配置错误。当你把 Open WebUI 放在像 Nginx Proxy Manager 这样的反向代理后面时,必须在 Open WebUI 配置中设置 CORS_ALLOW_ORIGIN 环境变量。

如果不这样做,即使你在 Nginx Proxy Manager 中启用了 “Websockets support”,WebSocket 连接也会失败。

HTTP/2 与 WebSockets

如果你在 Nginx 服务器上启用了 HTTP/2,请确保代理配置在连接 Open WebUI 后端时仍然使用 HTTP/1.1。这点非常重要,因为大多数 WebUI 功能(如流式传输和实时更新)都依赖 WebSocket,而在许多代理环境中,通过 HTTP/1.1 的 Upgrade 方式会比新的 RFC 8441(H2 上的 WebSocket)更稳定。

在 Nginx 的 location 块中,始终加入:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
关键:为 SSE 流式传输关闭代理缓冲

这是导致 Markdown 乱码和流式响应损坏的最常见原因。

当 Nginx 的 proxy_buffering 处于启用状态(默认就是启用!)时,它会随意重新分块 SSE 流。这会把 Markdown 标记拆散——例如 **bold** 会变成 ** + bold + **——从而导致输出损坏,出现可见的 ##** 或缺词。

你必须在 Nginx 的 location 块中加入以下指令:

# CRITICAL: Disable buffering for SSE streaming
proxy_buffering off;
proxy_cache off;

如果忘了这一步,典型症状包括:

  • 原始 Markdown 标记可见(##**###
  • 粗体/标题标记显示异常
  • 响应中随机缺少单词或段落
  • 关闭缓冲时正常,开启时就出问题

额外好处: 关闭缓冲后,流式响应还会明显更快,因为内容会直接流向客户端,不再经过 Nginx 的缓冲延迟。

请选择最适合你部署需求的方法。

Nginx Proxy Manager

Nginx Proxy Manager(NPM)可以让你轻松管理反向代理,并使用 Let's Encrypt 的有效 SSL 证书为本地应用(如 Open WebUI)提供保护。 这种配置可以启用 HTTPS 访问;由于许多移动浏览器的安全要求,这对于使用语音输入等功能很有必要,同时又不会把应用的特定端口直接暴露到互联网。

前提条件

  • 一台运行 Docker 的家用服务器,以及已经运行中的 open-webui 容器。
  • 一个域名(可以是 DuckDNS 这类免费域名,也可以是 Namecheap/GoDaddy 这类付费域名)。
  • 对 Docker 和 DNS 配置有基本了解。

Nginx Proxy Manager 步骤

  1. 创建 Nginx 文件目录:

    mkdir ~/nginx_config
    cd ~/nginx_config
  2. 使用 Docker 设置 Nginx Proxy Manager:

    nano docker-compose.yml
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    ports:
      - '80:80'
      - '81:81'
      - '443:443'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

启动容器:

docker-compose up -d
  1. 配置 DNS 和域名:

    • 登录你的域名提供商(例如 DuckDNS)并创建域名。
    • 将域名指向你的代理本地 IP(例如 192.168.0.6)。
    • 如果使用 DuckDNS,请从其仪表板获取 API token。

这里有一个简单示例,展示在 DuckDNS 域名页 中如何操作

  1. 设置 SSL 证书:
  • 使用 http://server_ip:81 访问 Nginx Proxy Manager。例如:192.168.0.6:81

  • 使用默认凭据登录(admin@example.com / changeme)。按提示修改它们。

  • 进入 SSL 证书 → 添加 SSL 证书 → Let's Encrypt

  • 填写你从 DuckDNS 获得的邮箱和域名。一个域名带星号,另一个不带。例如:*.hello.duckdns.orghello.duckdns.org

  • 选择 使用 DNS 验证,选择 DuckDNS,然后粘贴你的 API token。例如: dns_duckdns_token=f4e2a1b9-c78d-e593-b0d7-67f2e1c9a5b8

  • 同意 Let's Encrypt 条款并保存。若有需要,可调整传播时间(120 秒)。

  1. 创建 Proxy Host:
  • 对于每个服务(例如 openwebui、nextcloud),进入 主机 → 代理主机 → 添加代理主机

  • 填入域名(例如 openwebui.hello.duckdns.org)。

  • 协议选择 HTTP(默认),启用 WebSocket 支持,并指向你的 Docker IP(如果 open-webui 和 NGINX Manager 运行在同一台机器上,这个 IP 和前面一样,例如 192.168.0.6)。

  • 选择之前生成的 SSL 证书,强制启用 SSL,并开启 HTTP/2。

关键:为 WebSocket 连接配置 CORS

WebSocket 连接中非常常见、而且很难排查的问题,是跨源资源共享(CORS)策略配置错误。当你把 Open WebUI 放在 Nginx Proxy Manager 这样的反向代理后面时,必须在 Open WebUI 配置中设置 CORS_ALLOW_ORIGIN 环境变量。

如果不这样做,即使你在 Nginx Proxy Manager 中启用了 “WebSocket 支持”,WebSocket 连接也会失败。

关键:为流式传输关闭代理缓冲

这是导致 Markdown 乱码和流式响应损坏的最常见原因。

在 Nginx Proxy Manager 中,进入代理主机 → 高级 选项卡 → 并在 自定义 Nginx 配置 字段中添加以下指令:

proxy_buffering off;
proxy_cache off;

如果不这样做,Nginx 会重新分块 SSE 流,导致 Markdown 格式被打散(出现可见的 ##**、缺词等问题)。这也会让流式响应更快。

长补全的超时设置

复杂任务的长时间 LLM 补全(30 分钟以上)可能超过默认的 60 秒超时。请在 高级 选项卡 → 自定义 Nginx 配置 中添加以下指令:

proxy_read_timeout 1800;
proxy_send_timeout 1800;
proxy_connect_timeout 1800;

这会把超时时间设置为 30 分钟。你可以根据使用场景自行调整。

缓存最佳实践

虽然 Nginx Proxy Manager 会自动处理大部分配置,但你仍应注意:

  • 静态资源(CSS、JS、图片)默认会被缓存,以提升性能
  • 认证端点 永远不应被缓存
  • 如果你在 NPM 的 “高级” 选项卡中添加自定义缓存规则,请确保排除 /api//auth//signup//signin//sso//admin//signout//oauth//login//logout/ 等路径

默认的 NPM 配置已经正确处理了这些情况——只有在你非常清楚自己在做什么时,才去修改缓存规则。

示例: 如果你通过 https://openwebui.hello.duckdns.org 访问界面,那么必须设置:

CORS_ALLOW_ORIGIN="https://openwebui.hello.duckdns.org"

你也可以提供一个以分号分隔的允许域名列表。不要跳过这一步。

:::

  1. 把你的 URL 添加到 open-webui(否则会出现 HTTPS 错误):
  • 进入 open-webui → 管理面板 → 设置 → 常规
  • Webhook URL 文本框中输入你通过 Nginx 反向代理连接 open-webui 所用的 URL。例如:hello.duckdns.org(在这种配置下不是必需)或 openwebui.hello.duckdns.org(在这种配置下是必需的)。

访问 WebUI

你可以通过 HTTPS 访问 Open WebUI,例如 hello.duckdns.orgopenwebui.hello.duckdns.org(按你的实际配置而定)。

备注

防火墙提示:请注意,本地防火墙软件(如 Portmaster)可能会阻止 Docker 内部网络流量或所需端口。如果遇到问题,请检查防火墙规则,确保此配置所需的通信已被允许。

完整的优化版 NGINX 配置

本节提供一个适用于生产环境的 NGINX 配置,专门针对 Open WebUI 的流式传输、WebSocket 连接以及高并发部署进行了优化。

上游配置

定义一个带 keepalive 连接的 upstream,以降低连接建立开销:

upstream openwebui {
    server 127.0.0.1:3000;
    keepalive 128;              # Persistent connections
    keepalive_timeout 1800s;    # 30 minutes
    keepalive_requests 10000;
}

超时配置

长时间运行的 LLM 补全需要更长的超时时间:

location /api/ {
    proxy_connect_timeout 1800;   # 30 minutes
    proxy_send_timeout 1800;
    proxy_read_timeout 1800;
}

# WebSocket connections need even longer timeouts
location ~ ^/(ws/|socket\.io/) {
    proxy_connect_timeout 86400;  # 24 hours
    proxy_send_timeout 86400;
    proxy_read_timeout 86400;
}

请求头和请求体大小限制

防止大请求或 OAuth token 导致错误:

# In http {} or server {} block
client_max_body_size 100M;           # Large file uploads
proxy_buffer_size 128k;              # Large headers (OAuth tokens)
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
large_client_header_buffers 4 32k;

常见的流式传输误区

设置对流式传输的影响
application/json 上启用 gzip on🔴 为压缩而缓冲
proxy_buffering on🔴 缓冲整个响应
proxy_request_buffering on应该关闭
tcp_nodelay on🔴 最关键: 禁用 Nagle 算法,立即发送数据包(可避免 200ms 延迟)
chunked_transfer_encoding on🟡 可能破坏 SSE
/api/ 上启用 proxy_cache🟡 带来额外开销
X-Accel-Buffering "yes"为了更安全,这个头应设置为 no
HTTP/2如果遇到流式问题、延迟,或者前端在最后一个块到达前就结束了流式输出,改用 HTTP 1.1 而不是 HTTP/2 也可能有帮助

完整示例配置

upstream openwebui {
    server 127.0.0.1:3000;
    keepalive 128;
    keepalive_timeout 1800s;
    keepalive_requests 10000;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL 配置……

    # 压缩 - 排除流式内容类型
    gzip on;
    gzip_types text/plain text/css application/javascript image/svg+xml;
    # 不要包含:application/json、text/event-stream

    # API 端点 - 针对流式优化
    location /api/ {
        proxy_pass http://openwebui;
        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;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # 关键:为流式传输关闭所有缓冲
        gzip off;
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_cache off;
        tcp_nodelay on;
        add_header X-Accel-Buffering "no" always;
        add_header Cache-Control "no-store" always;

        # 为 LLM 补全设置更长的超时时间
        proxy_connect_timeout 1800;
        proxy_send_timeout 1800;
        proxy_read_timeout 1800;
    }

    # WebSocket 端点
    location ~ ^/(ws/|socket\.io/) {
        proxy_pass http://openwebui;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        gzip off;
        proxy_buffering off;
        proxy_cache off;

        # 为持久连接设置 24 小时超时
        proxy_connect_timeout 86400;
        proxy_send_timeout 86400;
        proxy_read_timeout 86400;
    }

    # 静态资源 - 可以缓冲和缓存
    location /static/ {
        proxy_pass http://openwebui;
        proxy_buffering on;
        proxy_cache_valid 200 7d;
        add_header Cache-Control "public, max-age=604800, immutable";
    }

    # 默认 location
    location / {
        proxy_pass http://openwebui;
        proxy_http_version 1.1;
        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;
    }
}

缓存配置

合理的缓存可以显著提升 Open WebUI 的性能,减少后端负载并加快页面加载。本节为希望实现服务端和客户端缓存的高级用户提供指导。

缓存区域

在 nginx 的 http 块中定义缓存区域,用于存储缓存响应:

# General cache for pages and assets
proxy_cache_path /var/cache/nginx/openwebui levels=1:2 
    keys_zone=OPENWEBUI_CACHE:10m max_size=1g inactive=60m use_temp_path=off;

# Dedicated cache for images (profile pictures, model avatars)
proxy_cache_path /var/cache/nginx/openwebui_images levels=1:2 
    keys_zone=OPENWEBUI_IMAGES:10m max_size=2g inactive=7d use_temp_path=off;
创建缓存目录

在 nginx 使用这些目录之前,你必须先创建它们并设置正确的所有权:

sudo mkdir -p /var/cache/nginx/openwebui /var/cache/nginx/openwebui_images
sudo chown -R www-data:www-data /var/cache/nginx

www-data 替换成你的 nginx 用户(可通过 ps aux | grep nginx 查看)。常见替代包括 nginxnobody

缓存哪些内容

内容类型缓存时长说明
静态资源(CSS、JS、字体)7–30 天对版本化资源使用 immutable
个人资料/模型图片1 天在新鲜度和性能之间取得平衡
静态文件(/static/)7 天Favicon、默认头像
HTML 页面5 分钟短缓存并进行重新验证
上传文件内容1 天用户上传内容、生成的图片

永远不要缓存什么

关键:绝不要缓存认证

以下路径绝不要缓存,以防止安全问题和登录异常:

  • /api/v1/auths/ - 认证端点
  • /oauth/ - OAuth/SSO 回调
  • /api/(通用)- 动态 API 响应
  • /ws/ - WebSocket 连接

认证端点始终应包含以下指令:

proxy_no_cache 1;
proxy_cache_bypass 1;
add_header Cache-Control "no-store, no-cache, must-revalidate";

示例:图片缓存

个人资料图片和模型头像从缓存中受益很大:

# User and model profile images
location ~ ^/api/v1/(users/[^/]+/profile/image|models/model/profile/image)$ {
    proxy_pass http://your_backend;
    
    proxy_cache OPENWEBUI_IMAGES;
    proxy_cache_valid 200 302 1d;
    proxy_cache_valid 404 1m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_lock on;
    proxy_cache_key "$request_uri$is_args$args";
    
    # 即使后端没有缓存头,也强制缓存
    proxy_ignore_headers Cache-Control Expires Set-Cookie;
    proxy_hide_header Set-Cookie;
    
    # 客户端缓存
    add_header Cache-Control "public, max-age=86400, stale-while-revalidate=604800" always;
    add_header X-Cache-Status $upstream_cache_status always;
    
    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 ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|otf|eot)$ {
    proxy_pass http://your_backend;
    
    proxy_cache OPENWEBUI_CACHE;
    proxy_cache_valid 200 302 60m;
    proxy_cache_valid 404 1m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_lock on;
    
    add_header Cache-Control "public, max-age=2592000";  # 30 days
    add_header X-Cache-Status $upstream_cache_status;
    
    etag on;
    if_modified_since exact;
}

缓存调试

添加 X-Cache-Status 头可以验证缓存是否生效:

add_header X-Cache-Status $upstream_cache_status always;

在浏览器 DevTools 中查看该头:

  • HIT - 由缓存提供
  • MISS - 从后端获取,随后写入缓存
  • EXPIRED - 缓存已过期并被刷新
  • BYPASS - 故意跳过缓存

权衡

缓存失效

当图片被强力缓存时,用户在更改头像后可能不会立即看到更新。可以考虑:

  • 如果用户经常更新图片,使用更短的缓存时间(例如 1 小时)
  • 如果部署比较稳定,使用更长的缓存时间(例如 1 天)以获得更好性能
  • 可以通过以下命令手动清空缓存:rm -rf /var/cache/nginx/openwebui_images/*

下一步

完成 HTTPS 配置后,可以通过以下地址安全访问 Open WebUI:

Ensure that your DNS records are correctly configured if you're using a domain name. For production environments, it's recommended to use Let's Encrypt for trusted SSL certificates.


本内容仅供参考,不构成任何保证、担保或合同承诺。Open WebUI 按“现状”提供。请参阅您的许可协议 以了解适用条款。