用 Caddy + Nginx 反向代理本地 LLM API
将 Ollama 或 vLLM 的 API 通过 Caddy 或 Nginx 反向代理到公网,配合自定义域名和 SSL 证书实现安全的远程调用。支持鉴权、流量限制、日志记录。
本地运行的 Ollama 或 vLLM 只能在局域网内访问。本教程教你用反向代理将其暴露到公网,支持自定义域名、自动 HTTPS(Let's Encrypt)、API 密钥鉴权。适用于远程开发、团队共享、移动端调用等场景。
Table of Contents
为什么需要反向代理
直接暴露本地端口的问题:
- ❌ 没有 SSL 证书,浏览器/客户端报安全警告
- ❌ 没有鉴权,任何人都能调用你的 API
- ❌ 没有流量控制,容易被滥用
- ❌ 没有日志记录,难以排查问题
- ❌ 公网 IP 贵且不稳定,家庭宽带多为 NAT
反向代理解决以上所有问题:
- ✅ 自动 HTTPS(Let's Encrypt)
- ✅ API 密钥验证
- ✅ 速率限制
- ✅ 访问日志
- ✅ 配合 Cloudflare Tunnel 无需公网 IP
| 对比项 | Caddy | Nginx | Cloudflare Tunnel |
|---|---|---|---|
| 配置难度 | ⭐ 最简单 | ⭐⭐⭐ 中等 | ⭐ 最简单 |
| 自动 HTTPS | ✅ 内置 | ❌ 需额外配置 | ✅ 内置 |
| 需要公网 IP | 是 | 是 | 否 |
| API 鉴权 | 插件/中间件 | ngx_http_auth_request | Access Policy |
方案一:Caddy(推荐)
Caddy 是最简单的方式,配置文件只需 5 行就能实现自动 HTTPS + 反向代理。
安装 Caddy
$
# Ubuntu/Debian
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy.list
sudo apt update && sudo apt install caddy编写 Caddyfile
# /etc/caddy/Caddyfile
# Ollama API(完整 OpenAI 兼容)
api.ollama.example.com {
reverse_proxy localhost:11434
encode gzip
log {
output file /var/log/caddy/ollama.log
}
}
# vLLM API
api.vllm.example.com {
reverse_proxy localhost:8000
encode gzip
log {
output file /var/log/caddy/vllm.log
}
}
启动 Caddy
$
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo systemctl enable --now caddy
Caddy 自动 HTTPS 生效!
Caddy 会自动从 Let's Encrypt 申请 SSL 证书。访问
Caddy 会自动从 Let's Encrypt 申请 SSL 证书。访问
https://api.ollama.example.com 即可看到 Ollama 的 API 响应。
方案二:Nginx
安装 Nginx
$
sudo apt update && sudo apt install -y nginx certbot python3-certbot-nginx配置反向代理
创建 /etc/nginx/sites-available/ollama:
server {
listen 80;
server_name api.ollama.example.com;
location / {
proxy_pass http://127.0.0.1:11434;
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;
proxy_cache_bypass $http_upgrade;
# 超时设置(LLM 响应可能很慢)
proxy_read_timeout 300s;
proxy_connect_timeout 75s;
proxy_send_timeout 300s;
# 关闭请求体大小限制
client_max_body_size 100M;
}
access_log /var/log/nginx/ollama_access.log;
error_log /var/log/nginx/ollama_error.log;
}
启用站点并申请 SSL
$
sudo ln -s /etc/nginx/sites-available/ollama /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
# 申请 SSL 证书
sudo certbot --nginx -d api.ollama.example.com自动续期 SSL
$
sudo certbot renew --dry-run
Certbot 自动续期
Certbot 会自动添加 cron job 到
Certbot 会自动添加 cron job 到
/etc/cron.d/certbot。SSL 证书每 90 天自动续期,无需手动操作。
API 密钥鉴权
如果不想公开 API,需要添加鉴权。
Nginx + HTTP Basic Auth
$
# 创建密码文件
sudo apt install -y apache2-utils
sudo htpasswd -bc /etc/nginx/.htpasswd admin your_secure_password
# 修改 Nginx 配置,在 location 内添加:
# auth_basic "Restricted API";
# auth_basic_user_file /etc/nginx/.htpasswd;
sudo nginx -t && sudo systemctl reload nginxNginx + Bearer Token
使用 Lua 模块实现 Bearer Token 验证:
server {
listen 80;
server_name api.ollama.example.com;
# 验证 Bearer Token
location / {
if ($http_authorization !~ "^Bearer sk-ollama-secret-key-12345") {
return 401 '{"error": "Unauthorized"}';
}
proxy_pass http://127.0.0.1:11434;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Caddy + Basic Auth
api.ollama.example.com {
basic_auth {
admin JDJhJDEwJEVCc0wuUUtxcG9LUXVNZVlxLkRLTGg=
}
reverse_proxy localhost:11434
}
客户端调用方式
$
# curl 调用
curl https://api.ollama.example.com/v1/chat/completions \
-H "Authorization: Bearer sk-ollama-secret-key-12345" \
-H "Content-Type: application/json" \
-d '{"model": "llama3.2:3b", "messages": [{"role": "user", "content": "Hello"}]}'Cloudflare Tunnel(零部署)
如果你没有公网服务器或不想暴露端口,Cloudflare Tunnel 是最简单方案。零配置、不需要公网 IP。
安装 cloudflared
$
# Linux
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
chmod +x cloudflared
sudo mv cloudflared /usr/local/bin/
# macOS
brew install cloudflare/cloudflare/cloudflared创建 Tunnel
$
# 登录 Cloudflare(浏览器会自动打开)
cloudflared tunnel login
# 创建隧道
cloudflared tunnel create ollama-proxy
# 查看隧道 ID
cloudflared tunnel list配置 Tunnel
# ~/.cloudflared/config.yml
tunnel: <TUNNEL_ID>
credentials-file: ~/.cloudflared/<TUNNEL_ID>.json
ingress:
- hostname: api.ollama.example.com
service: http://localhost:11434
- service: http_status:404
启动 Tunnel
$
# 前台运行测试
cloudflared tunnel run ollama-proxy
# 后台运行
cloudflared service install
Zero Trust 策略
在 Cloudflare Zero Trust Dashboard 中可以给 Tunnel 绑定 Access Policy,只允许特定邮箱/IP 访问,或添加 OTP 验证。
在 Cloudflare Zero Trust Dashboard 中可以给 Tunnel 绑定 Access Policy,只允许特定邮箱/IP 访问,或添加 OTP 验证。
测试与验证
测试本地 Ollama
$
curl http://localhost:11434/api/tags测试公网 API
$
# 测试 OpenAI 兼容端点
curl https://api.ollama.example.com/v1/models \
-H "Authorization: Bearer sk-ollama-secret-key-12345"
# 测试完整对话
curl https://api.ollama.example.com/v1/chat/completions \
-H "Authorization: Bearer sk-ollama-secret-key-12345" \
-H "Content-Type: application/json" \
-d '{
"model": "llama3.2:3b",
"messages": [{"role": "user", "content": "Say hello in 5 words"}],
"max_tokens": 20
}'测试 Claude App 接入
$
# 在 Claude Desktop 中配置
# File: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"ollama": {
"command": "ollama",
"args": ["serve"],
"env": {
"OLLAMA_HOST": "https://api.ollama.example.com"
}
}
}
}安全注意事项
暴露本地服务到公网的风险
请务必配置 API 鉴权!没有鉴权的 LLM API 可能被他人滥用,产生额外费用或被用于生成有害内容。
请务必配置 API 鉴权!没有鉴权的 LLM API 可能被他人滥用,产生额外费用或被用于生成有害内容。
安全清单
- ✅ 始终启用 API 密钥鉴权
- ✅ 使用强密码(20+ 字符随机字符串)
- ✅ 限制 IP 访问(在 Cloudflare Access Policy 中)
- ✅ 启用速率限制(防止滥用)
- ✅ 定期检查访问日志
- ✅ 不要将 .htpasswd 或 Token 提交到 Git
- ✅ 防火墙只开放 80/443,关闭 11434 直接访问
防火墙配置
$
# 只允许本地访问 Ollama 端口
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 11434/tcp
sudo ufw enableAdvertisement