1. 为什么 OpenClaw 一上公网就“断连”?先搞懂安全上下文
OpenClaw 是一个可本地部署的开源 AI 助手网关,能对接多种大模型、挂载工具链、跑 Agent 任务,适合想在自己服务器上掌控数据与调用链路的开发者。它自带 Web 控制台,默认监听 18789 端口。很多人第一次把它跑在云服务器上,浏览器打开http://公网IP:18789,界面能加载,但登录后立刻弹出一行红字:
disconnected (1008): control ui requires HTTPS or localhost (secure context)这不是 OpenClaw 的 bug,而是浏览器的硬性规则。OpenClaw 的控制台用 Web Crypto API 做设备身份校验,而 Web Crypto 只在**安全上下文(Secure Context)**里可用。安全上下文只有两种来源:一是localhost/127.0.0.1,二是 HTTPS。你用公网 IP 走 HTTP,浏览器直接判定“不安全”,加密接口全部拒绝,WebSocket 握手也就跟着断了。
所以“部署 OpenClaw”这件事,真正卡人的不是装不装得上,而是怎么让公网访问落在 HTTPS 上。这篇就按 Docker + Nginx 反代的路线,把证书挂载、反代配置、TaoToken 统一 Key 接入一次讲透,最后给你能直接复制的 curl 验证动作。
先说清楚本文适合谁:已经在服务器上跑起 OpenClaw 容器、手里有域名、想用一把统一 Key 调多家模型的人。如果你还没装 Docker,先补docker compose version能正常输出再往下看。
TaoToken 在这里的角色是“统一模型入口”。OpenClaw 支持自定义 OpenAI 兼容的 Base URL,你把 TaoToken 的 API 地址填进去,就能用同一个 Key 调用不同厂商的模型,不用在 OpenClaw 里维护一堆厂商 Key。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 根地址是 https://taotoken.net/api 。
下面按“先跑通 HTTPS,再接通模型”的顺序来,每一步都给可复制的片段。
2. Docker Compose 起 OpenClaw:端口、卷与网关 Token 怎么设
先把容器跑起来,并且只绑定回环地址,让 Nginx 来做对外入口。这样 18789 不直接暴露公网,安全面小很多。
新建目录并写docker-compose.yml:
services: openclaw: image: ghcr.io/1186258278/openclaw-zh:latest container_name: openclaw restart: unless-stopped ports: - "127.0.0.1:18789:18789" volumes: - ./data:/root/.openclaw - ./certs:/certs:ro environment: - TZ=Asia/Shanghai - OPENCLAW_GATEWAY_TOKEN=换成你自己的长随机串几个关键点逐个说。
ports写成127.0.0.1:18789:18789,意思是宿主机只监听本地回环。外网访问 18789 会直接被拒,所有流量必须经过 Nginx。这是生产部署的基本姿势,别图省事写成18789:18789。
./data:/root/.openclaw把配置目录挂出来,后面改openclaw.json不用进容器。./certs:/certs:ro是给内置 TLS 方案预留的证书目录,走 Nginx 反代时其实用不到,但留着不碍事,只读挂载更安全。
OPENCLAW_GATEWAY_TOKEN是网关访问令牌,登录控制台要输。别用your-secure-token这种示例值,用openssl rand -hex 32生成一串。
启动:
docker compose up -d docker compose logs -f openclaw日志里看到网关监听 18789、没有报错,就说明容器起来了。此时在服务器本机执行:
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:18789返回 200 或 302 都算正常。如果返回 000,说明容器没起或端口没通,先docker compose ps看状态。
接下来配置 OpenClaw 信任反向代理。编辑./data/openclaw.json,至少要有这些字段:
{ "gateway": { "bind": "loopback", "port": 18789, "trustedProxies": ["127.0.0.1", "::1"], "auth": { "mode": "token", "token": "换成你自己的长随机串" }, "controlUi": { "allowedOrigins": ["https://你的域名"] } } }trustedProxies必须包含127.0.0.1,否则 Nginx 转发过来的请求会被判定为“来自不可信地址”,报Proxy headers detected from untrusted address。allowedOrigins填你的 HTTPS 域名,解决跨域。改完重启:
docker compose restart openclaw到这里容器侧就绪。下一步是 Nginx 和证书。
3. Nginx 反代 + 证书挂载:可复制的 server 配置片段
先装 Nginx 和 Certbot。Ubuntu / Debian:
sudo apt update sudo apt install -y nginx certbot python3-certbot-nginx sudo systemctl enable nginx sudo systemctl start nginx写配置文件/etc/nginx/sites-available/openclaw。注意 WebSocket 升级头是重中之重,OpenClaw 控制台靠长连接实时刷新,缺了 Upgrade 头就会反复断连。
map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; server_name 你的域名; return 301 https://$server_name$request_uri; } server { listen 443 ssl; http2 on; server_name 你的域名; ssl_certificate /etc/letsencrypt/live/你的域名/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/你的域名/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; add_header X-Content-Type-Options nosniff; location / { proxy_pass http://127.0.0.1:18789; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $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; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; proxy_read_timeout 86400s; proxy_send_timeout 86400s; proxy_connect_timeout 75s; proxy_buffering off; } }proxy_buffering off对实时交互很关键,开着缓冲会让流式输出一顿一顿。proxy_read_timeout 86400s是给长任务留足时间,Agent 跑几分钟很正常。
启用站点并测试:
sudo ln -s /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginxnginx -t必须返回syntax is ok和test is successful,有报错先修再 reload。
申请证书:
sudo certbot --nginx -d 你的域名Certbot 会自动把证书路径写进配置并设置续期定时器。验证续期:
sudo certbot renew --dry-run看到Congratulations, all simulated renewals succeeded就放心了。证书 90 天有效,自动续期别关。
如果你用 Docker 跑 Nginx,证书挂载要写进 compose:
nginx: image: nginx:stable ports: - "80:80" - "443:443" volumes: - ./nginx/openclaw.conf:/etc/nginx/conf.d/openclaw.conf:ro - /etc/letsencrypt:/etc/letsencrypt:ro depends_on: - openclaw注意容器里的 Nginx 访问宿主机上的 OpenClaw,proxy_pass不能写127.0.0.1,要写宿主机在 Docker 网络里的地址,或者把 OpenClaw 和 Nginx 放进同一个 compose 网络,用服务名http://openclaw:18789。这是很多人踩的坑:配置照抄,结果 502。
4. 接入 TaoToken 统一 Key:Base URL、Key 与 Model ID 三件套
HTTPS 通了之后,控制台能登录了,但模型还没接。OpenClaw 支持 OpenAI 兼容协议,TaoToken 正好提供这个入口,所以配置很直接。
在 OpenClaw 控制台里找到模型 / Provider 设置,或者直接改openclaw.json的模型段。核心就三样:Base URL、API Key、Model ID。
{ "models": { "providers": { "taotoken": { "type": "openai-compatible", "baseUrl": "https://taotoken.net/api", "apiKey": "你的TaoToken Key", "models": [ { "id": "claude-sonnet-4-5", "name": "Claude Sonnet" }, { "id": "gpt-4o-mini", "name": "GPT-4o mini" } ] } } } }Base URL 写https://taotoken.net/api,不要多加/v1之类的后缀,OpenClaw 会按 OpenAI 兼容规范自己拼路径。Key 在控制台的 API Keys 页面生成,入口是 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。Model ID 要填 TaoToken 支持的模型标识,填错会报model not found。
改完重启容器:
docker compose restart openclaw如果你用的是 Claude Code 这类需要单独配置的客户端,TaoToken 也提供对应的接入文档,路径在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,里面有 Base URL、Key、Model ID 的完整填法。长期跑编码任务或 Agent 的,可以看 Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,按用量选更划算。
配置完先别急着在界面里点,用 curl 直接打 TaoToken 的接口,确认 Key 本身有效:
curl -s https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer 你的TaoToken Key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "ping"}] }'返回里带choices数组和内容,说明 Key 和模型都通。如果返回 401,是 Key 问题;返回model not found,是 Model ID 写错。这一步单独验证,能把“网络问题”和“配置问题”分开,排障快很多。
5. 验证 HTTPS 与 Key 生效:curl 检查动作与常见报错对照
部署完别只看浏览器,用 curl 做几组检查,结果可复现。
第一组,验证 HTTPS 和证书链:
curl -sS -o /dev/null -w "http_code=%{http_code} ssl_verify=%{ssl_verify_result}\n" \ https://你的域名http_code=200或302、ssl_verify=0表示证书有效、链路正常。ssl_verify非 0 说明证书链有问题,多半是 fullchain 没配对。
第二组,验证 HTTP 跳转 HTTPS:
curl -sS -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://你的域名应返回301 -> https://你的域名/。
第三组,验证 WebSocket 升级头是否透传:
curl -sS -i -N \ -H "Connection: Upgrade" \ -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ https://你的域名/ | head -n 20看到101 Switching Protocols就说明反代的 Upgrade 头生效了。如果返回 400 或直接断开,回去检查 Nginx 里的map和两个proxy_set_header。
下面把常见报错和对应原因列成表,方便对照。
| 报错信息 | 触发位置 | 原因 | 处理 |
|---|---|---|---|
disconnected (1008): control ui requires HTTPS or localhost | 浏览器控制台 | 用 HTTP 访问公网 IP | 配好 HTTPS 或改用 localhost |
401 Unauthorized | TaoToken 接口 | Key 错误或未带 Authorization | 检查 Key 与请求头 |
local proxy failed | OpenClaw 网关 | 反代地址或网络不通 | 确认proxy_pass指向可达地址 |
reading 'choices' | 模型调用 | 返回体不是预期结构 | 核对 Base URL 与 Model ID |
Proxy headers detected from untrusted address | OpenClaw 日志 | 未配trustedProxies | 加入127.0.0.1、::1 |
OAuth相关报错 | 客户端登录 | 认证模式不匹配 | 确认auth.mode与客户端一致 |
| 502 Bad Gateway | Nginx | 后端容器不可达 | 检查容器网络与服务名 |
local proxy failed和reading 'choices'这两个最容易混。前者是 OpenClaw 到模型入口的网络层失败,后者是请求发出去了但响应结构不对。用第 4 节的 curl 单独打 TaoToken,能快速定位是哪一层。
还有一个隐蔽问题:改了openclaw.json但没重启容器,配置不生效。养成docker compose restart openclaw的习惯,改完就看日志确认加载成功。
6. 把安全链路固定下来:续期、备份与统一入口
跑通之后,把几件事固定成习惯,省得后面返工。
证书续期交给 Certbot 的 timer,但建议每月手动跑一次sudo certbot renew --dry-run确认没坏。Nginx 配置改动后永远先nginx -t再 reload,别直接 restart。
./data目录定期备份,里面是 OpenClaw 的配置和会话数据。备份前先docker compose stop openclaw,避免写一半拷走。
模型入口统一到 TaoToken 之后,换模型只改 Model ID,不用动 Key 和 Base URL。需要看模型列表或临时对话验证,走模型对话入口 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ;要管理 Key 走 API Keys 页面;接入细节查文档。这样一套 HTTPS + 统一 Key 的链路,换服务器、换域名都能照着复现。
最后留一个实操建议:把第 5 节那三条 curl 写成一个check.sh,每次改完配置跑一遍,比在浏览器里反复刷新快得多,也更容易看出是哪一层出的问题。