独立的 OpenAI-compatible API 反向代理,用来把你的网站 API 请求转发到上游服务,同时保存请求/响应,并按规则改 JSON 参数。
默认上游:
https://api.finalval.com
默认本地监听:
http://127.0.0.1:8787
需要 Node.js 22.5 或更新版本,因为面板索引使用了内置的 node:sqlite。
npm start最小 watchdog:
.\start-watchdog.ps1停止 watchdog:
.\stop-watchdog.ps1如需注册为当前用户登录后自动启动的计划任务:
.\install-watchdog-task.ps1自定义端口或上游:
$env:PORT="8787"
$env:UPSTREAM_ORIGIN="https://api.finalval.com"
npm start你的站点原来如果请求:
https://api.finalval.com/v1/models
改成:
http://127.0.0.1:8787/v1/models
API key 仍然按原来的 Authorization: Bearer ...、x-api-key 或 x-goog-api-key 传。
当前这台机器的 sub2api 直连入口是:
https://api.finalval.com -> nginx -> http://127.0.0.1:18080 -> sub2api
透明抓包代理只用于需要捕获请求的公网项目,目标入口:
https://proxy.finalval.com -> nginx -> http://127.0.0.1:8787 -> http://127.0.0.1:18080 -> sub2api
为了降低延迟,生产代理容器使用只抓包不改参模式:
CAPTURE_ONLY=1
CHAT_COMPLETIONS_STREAM_MODE=off
UPSTREAM_ORIGIN=http://127.0.0.1:18080
该模式不会读取完整请求后再转发,也不会解析 JSON 或加载 rules.json;请求体和响应体会边转发边复制一份用于 capture。这样大请求不会额外等待一次完整上传。
启动/停止生产代理:
sudo systemctl start api-https-proxy.service
sudo systemctl stop api-https-proxy.service
sudo systemctl status api-https-proxy.service底层使用 Docker Compose:
sudo docker compose -f /home/ubuntu/api-https/docker-compose.proxy.yml ps
sudo docker logs -f api-https-proxy公网域名 nginx 配置在:
/etc/nginx/sites-available/proxy-finalval.conf
仓库副本在:
nginx/proxy-finalval.conf
/panel 和 /panel-api/ 默认只允许本机访问;公网只用于 API 请求透传。proxy.finalval.com 需要 A 记录指向本机公网 IP 后,才能用 certbot 签发 HTTPS 证书。
DNS 到位后签发证书:
sudo certbot --nginx -d proxy.finalval.com --redirect
sudo nginx -t
sudo systemctl reload nginx也可以使用仓库里的检查脚本:
sudo /home/ubuntu/api-https/scripts/issue-proxy-cert.sh抓包面板通过 sub2api 后台里的 admin-only 自定义菜单进入:
api.finalval.com -> /custom/proxy-panel -> iframe /_proxy-panel/
实际反代链路:
https://api.finalval.com/_proxy-panel/
-> nginx
-> http://127.0.0.1:8787/_proxy-panel/
-> panel
/_proxy-panel/ 会读取 sub2api 自定义页面 iframe 自动追加的 token 参数,并调用上游 http://127.0.0.1:18080/api/v1/auth/me 校验。只有 role=admin 的 sub2api 用户会被换成短期 HttpOnly 面板会话 cookie;无 token、无效 token、非管理员 token 都返回 403。
公网 https://proxy.finalval.com/panel 仍然不开放,面板只通过 api.finalval.com/_proxy-panel/ 内嵌访问。
每次请求会保存到:
captures/YYYY-MM-DD/*.json
面板运行时还会维护:
captures/.panel-index.sqlite
captures/YYYY-MM-DD/_assets/*
- 新捕获中的内联图片会在保存时拆成独立资产文件,JSON 里只保留
/panel-api/capture-asset?...引用 - 面板摘要列表走 SQLite 索引,不再每次刷新全量解析所有大 JSON
- 启动后会后台迁移旧格式大文件,把历史里的内联图片也拆成独立资产文件
默认会在捕获文件里隐藏 authorization、x-api-key、x-goog-api-key、cookie、set-cookie 等敏感 header。转发给上游的真实请求不会被隐藏。
本地 Web 面板:
http://127.0.0.1:8787/panel
面板只读展示捕获列表、请求/响应内容、转发 body 和命中的修改规则。当前 Cloudflare 白名单没有暴露 panel 路径。
面板还支持按时间范围导出会话 Markdown:
- 可指定
From / To时间段 - 会按“后一次请求包含前面对话历史”的特征重建自然对话流
- 导出为
.md,尽量保留原始 Markdown、代码块、列表和图片/文件说明 - 适合把一整段对话流程汇总出来,而不是按单次请求逐条看
如需保存完整 header:
$env:REDACT_HEADERS="0"
npm start对 /v1/chat/completions:
- 原生
stream: true请求保持不变 - 非流式请求会按条件自动改成“上游流式、下游回普通 JSON”
- 这样可以显著减少大图/大文本请求卡在
response headers的问题
默认自动判定阈值:
CHAT_COMPLETIONS_STREAM_MODE=auto
CHAT_COMPLETIONS_STREAM_THRESHOLD_BYTES=49152
CHAT_COMPLETIONS_STREAM_THRESHOLD_IMAGES=1
CHAT_COMPLETIONS_STREAM_THRESHOLD_MESSAGES=12
可选模式:
CHAT_COMPLETIONS_STREAM_MODE=off
CHAT_COMPLETIONS_STREAM_MODE=auto
CHAT_COMPLETIONS_STREAM_MODE=always
如果你明确知道某条上游对非流式很差,可以设成:
$env:CHAT_COMPLETIONS_STREAM_MODE="always"
npm start如果想更保守,只在很大的请求上启用桥接,可以调高阈值:
$env:CHAT_COMPLETIONS_STREAM_THRESHOLD_BYTES="262144"
$env:CHAT_COMPLETIONS_STREAM_THRESHOLD_IMAGES="4"
npm start默认启用保留策略,避免捕获长期无限增长:
CAPTURE_RETENTION_DAYS=30
CAPTURE_RETENTION_MAX_FILES=800
CAPTURE_RETENTION_MAX_TOTAL_MB=2048
命中任一条件后,会自动清理较旧 capture,并连带删除其拆出的图片资产与 SQLite 索引记录。
如果你想关掉某一项,把它设为 0 即可:
$env:CAPTURE_RETENTION_DAYS="0"
$env:CAPTURE_RETENTION_MAX_FILES="0"
$env:CAPTURE_RETENTION_MAX_TOTAL_MB="0"
npm start- watchdog 每
5秒检查一次127.0.0.1:8787 - 如果代理没在监听,会自动调用
start-proxy.ps1 - 运行状态文件:
.proxy.pid
.watchdog.pid
watchdog.log
watchdog.stdout.log
watchdog.stderr.log
改 rules.json。例如强制把 chat completions 的 temperature 改成 0.2:
{
"name": "force-temperature",
"enabled": true,
"methods": ["POST"],
"pathPrefix": "/v1/chat/completions",
"jsonSet": {
"temperature": 0.2
},
"jsonDelete": [],
"modelMap": {}
}模型名映射示例:
"modelMap": {
"gpt-4o": "gpt-5.4"
}删除字段示例:
"jsonDelete": ["stream_options", "metadata.debug"]规则文件每次请求都会重新读取,修改后不用重启代理。
代理默认会返回 CORS 头,方便浏览器前端直接请求:
Access-Control-Allow-Origin: *
如果你的网页本身是 HTTPS,浏览器通常不允许它直接请求本地 HTTP 地址,这属于 mixed content 限制。生产网站建议把这个代理部署到一个 HTTPS 地址,或者放到网站自己的后端/API route 里,再把前端 API base URL 指过去。
这个项目是反向代理:你把网站里的 API base URL 指向代理,代理才能看到和修改明文 JSON。
如果想在“不改网站代码、不改 API base URL”的情况下截获 HTTPS 明文,那是 MITM 代理,需要安装本地 CA 证书或控制 DNS/域名证书信任链,复杂度和风险都高很多,不建议作为第一版。