项目地址: github.com/qtopie/vproxy
引言:为什么我们需要在透明代理中做接口重写?
在日常前端、移动端以及全栈开发联调中,我们经常遇到这样的经典场景:
- 线上排查或本地开发:想直接在真实线上网站或 Staging 环境调试前端代码,把线上的
bundle.js/app.css替换为本地 Vite / Webpack 开发服务器(http://127.0.0.1:3000)实时编译的代码,但后端真实接口依然走线上生产集群。 - 后端接口联调:客户端 App 或网页发出的某些生产 API,需要透明重定向到本地开发机(
http://127.0.0.1:8080)正在断点调试的服务上,而不需要在客户端代码里硬编码修改基础 URL。 - 接口数据 Mock:后端某些新接口尚未联调或报错,需要快速用本地的一个静态 JSON 文件临时 Mock 响应。
以往,大家通常会使用 Whistle 或 Charles。但这类传统调试工具最大的痛点在于接入成本与侵入性:
- 必须给浏览器安装 SwitchyOmega 插件,或修改操作系统全局代理;
- 许多原生命令行程序、Electron 应用、后台守护进程以及移动端模拟器根本不遵循系统代理,抓包和重定向极其繁琐;
- 传统的 Hosts 文件只能按域名整体修改 IP,无法细化到单个 URL 路径,一改就会导致整个域名的所有接口全部打崩。
vproxy 作为一个基于 Linux eBPF / macOS utun / Windows Wintun 的高性能透明代理工具,天然具备“对应用完全透明、无需配置代理环境变量”的内核级优势。
在新版本中,vproxy 正式引入了全新的 L7 应用层请求重写引擎(rewrites),将内核级透明代理与类似 Whistle 的便捷规则相结合,实现了真正意义上的 “零配置、全透明、按路径/正则自由重写与本地 Mock”。
核心设计:代理分流与请求重写的彻底解耦
在以往的代理工具设计中,网络分流规则与应用层报文修改常常混杂在一起。vproxy 在架构上将它们彻底分离为两层流水线:
======================================================
L4 传输层分流 (rules)
======================================================
负责系统流量的网络边界决策:
- 直连 (DIRECT) vs 上游出海节点 (PROXY)
- 域名后缀、IP-CIDR 以及进程可执行文件路径匹配 (PROCESS)
│
▼
======================================================
L7 应用层重写 (rewrites)
======================================================
负责 HTTP/HTTPS 应用层报文的精细化劫持:
- 正则表达式与通配符路径匹配
- 自动 TLS 证书卸载与跨协议转发 (HTTPS -> HTTP)
- 本地静态 Mock 文件挂载 (file://)
在配置文件 vproxy.json 中,两者的职责一目了然:
{
"upstreams": [
"socks5://127.0.0.1:1080"
],
// 1. L4 代理分流:决定哪些流量走上游代理翻墙,哪些直连
"rules": [
"google.com,PROXY",
"PROCESS,Telegram,PROXY",
"FINAL,DIRECT"
],
// 2. L7 请求重写:类 Whistle 语法,负责本地热替换与接口代理
"rewrites": [
"/cafe123.cn\\/.*?\\.(html|js|css|png|jpg)/ http://127.0.0.1:3000",
"https://api.prod.com/v1/* http://127.0.0.1:8080/$1",
"https://api.prod.com/v1/config file:///home/user/mock/config.json"
]
}
核心特性与语法详解
1. 类 Whistle 正则替换(自动追加 URL 路径)
前端开发者最常用的模式是把线上域名的所有静态资源打到本地开发服务器:
/cafe123.cn\/.*?\.(html|js|css|png|jpg)/ http://127.0.0.1:3000
- 匹配规则:使用前后斜杠
/.../包裹的正则表达式; - 自动路径透传(Auto Path Append):当目标地址(如
http://127.0.0.1:3000)没有显式指定子路径时,vproxy会自动将原请求的Path + Query拼接到本地服务末尾; - 请求示例:
- 请求:
https://cafe123.cn/assets/index-b4f2.js?t=171000 - 自动重写并转发给:
http://127.0.0.1:3000/assets/index-b4f2.js?t=171000
- 请求:
2. 正则捕获组变量回填($1, $2)
当目标 URL 需要重新调整目录结构时,可以使用正则分组变量:
/api.example.com\/v1\/(.*)/ http://127.0.0.1:8080/v2/$1
- 请求示例:
- 请求:
https://api.example.com/v1/user/profile?id=42 - 自动提取
$1并回填为:http://127.0.0.1:8080/v2/user/profile?id=42
- 请求:
3. 通配符路径替换(*)
除了正则表达式外,也支持简单直观的 * 通配符:
https://api.prod.com/service/* http://127.0.0.1:9000/$1
- 匹配
https://api.prod.com/service/order/create; - 展开后打给:
http://127.0.0.1:9000/order/create。
4. 本地静态文件 Mock(file://)
无需启动任何后端服务,直接将某个线上 API 映射为本地磁盘上的任意文件:
https://api.prod.com/v1/app_init file:///home/qtopie/mock/init.json
- 当请求触发时,
vproxy会拦截请求,直接读取本地文件并以 HTTP 200 返回; - 自动根据文件后缀(如
.json、.html、.png)识别并返回正确的Content-Type; - 默认自动添加跨域头
Access-Control-Allow-Origin: *,防止本地调试遇到 CORS 跨域限制。
杀手锏:全透明代理下的“跨协议反向代理”
在本地联调中最头疼的问题之一是 SSL 证书:
- 浏览器访问线上生产域名必然走 HTTPS;
- 但本地 Vite / Go / Node 服务通常只监听 明文 HTTP(如
http://127.0.0.1:3000),极少有人会给本地配置合法的 TLS 证书。
vproxy 内置了动态 CA 证书系统与协议转换层:
[浏览器 / 任意客户端]
│ HTTPS 请求 (https://cafe123.cn/app.js)
▼
======================================================
vproxy 本地透明代理内核
======================================================
1. 嗅探 SNI: cafe123.cn
2. 命中 rewrites 规则列表
3. 使用内置根 CA 动态伪造目标域名证书完成 TLS 握手
4. 终结 (Terminate) TLS,解密得到明文 HTTP Request
5. 自动注入透传头:
- X-Forwarded-Proto: https
- X-Forwarded-Host: cafe123.cn
6. 转换为明文 HTTP 转发给本地服务器
│
▼ HTTP 请求 (http://127.0.0.1:3000/app.js)
[本地开发服务 (Vite / Node / Go)]
客户端感受不到任何异常,完全以原生 HTTPS 与 vproxy 通信,而本地服务也无需配置复杂的 SSL 证书即可正常接收明文请求。
性能保障:Host 预筛选分桶索引(Host-Bucket Indexing)
很多开发者担心:“如果我在配置里写了大量复杂的正则表达式,会不会让全网代理的性能急剧下降?”
vproxy 采用了工业级的 Host 预筛选分桶机制,从根本上杜绝了正则的无谓开销:
- 规则静态解析:
在加载
rewrites规则时,静态分析提取出规则绑定的专属主机域名(例如从/cafe123.cn\/.../提取出cafe123.cn); - $O(1)$ 哈希分桶:
把属于
cafe123.cn的正则规则单独存放在hostBuckets["cafe123.cn"]中; - 极速跳过无关流量:
当用户在访问
google.com、github.com或bilibili.com时,vproxy根据请求的Host一次哈希查桶,发现该域名没有任何 rewrite 规则,直接短路跳过,0 正则比对,耗时完全是纳秒级! - 离线与断网容错:
即便某个被拦截的域名在真实公网 DNS 中不存在(或者是内网自定义假域名),或者外部网络完全断开,只要配置了重写到本地服务,
vproxy都不会去等待上游 DNS 超时,直接秒级转发到本地!
实战演练:三分钟上手
步骤 1:安装根证书(仅首次需要)
为了让浏览器信任 vproxy 动态伪造的 HTTPS 解密证书,首次使用时只需将根证书导入系统:
- 根证书路径:
/tmp/vproxy-ca.crt - 在 macOS 上双击加入“钥匙串访问”并设为“始终信任”;在 Linux 上复制到
/usr/local/share/ca-certificates/并执行update-ca-certificates;在 Windows 上导入“受信任的根证书颁发机构”。
步骤 2:编辑 vproxy.json
在当前目录或 ~/.vproxy/config.json 中配置你的重写规则:
{
"upstreams": [
"socks5://127.0.0.1:1080"
],
"rules": [
"FINAL,DIRECT"
],
"rewrites": [
"/my-company.com\\/assets\\/.*\\.(js|css)/ http://127.0.0.1:3000",
"https://api.my-company.com/v1/user/info http://127.0.0.1:8080/v1/user/info"
]
}
步骤 3:启动 vproxy
# 启动透明代理服务
sudo vproxy start
步骤 4:体验零侵入热替换
此时你不需要在 Chrome 里装任何插件,也不需要修改系统代理:
- 打开浏览器访问
https://my-company.com; - 页面中请求的静态 JS/CSS 会被秒级重定向到本地
http://127.0.0.1:3000(修改本地代码页面立即生效热重载); - 打开 Web 控制台(浏览器访问
http://127.0.0.1:9999),可以在实时 Tracing 面板中清晰看到每一笔被重写拦截的请求耗时与目标映射!
总结
通过将 L4 传输层分流 与 L7 接口重写 深度解耦,vproxy 不仅是一个极致轻量、跨平台的内核级透明翻墙工具,更升级为了现代全栈与前端开发者的联调利器。
欢迎前往 GitHub 体验并提出建议:github.com/qtopie/vproxy。