Manifest V3 · 只存本地 · MIT 开源

一条规则,让 FAT 前端直连 UAT 后端——不改代码、不改 CORS

跨域代理助手是一款开源 Chrome 扩展(Manifest V3),把页面发出的 API 请求代理到另一个后端环境。重写 URL、请求头与响应,条件化 Mock、注入延迟、阻断请求、失败重试、转发 WebSocket——全部在浏览器里配置,数据只存在你的电脑上。

需要较新版本的桌面 Google Chrome(Manifest V3)。推荐通过 Chrome Web Store 安装,也可以从源码构建或下载预构建包。

无埋点无遥测 不需要后端改动 网络层零 JS 快通道 HAR · cURL · JSON 中英双语界面 6 套主题
FAT 页面 · fat.appfetch · XMLHttpRequest · WebSocket
简单规则 → DNR 重定向网络层完成 · 单请求零 JS 开销
复杂规则 → SW 通道改写 · Mock · 延迟 · 阻断 · 重试 · ws
目标环境UAT · PROD · Mock 服务——全浏览器共用一套规则
一套规则集,两条转发通道——按规则区分,而不是按项目区分。拦截失败时页面会回退到原生 fetch / XMLHttpRequest / WebSocket;被阻断的请求绝不回退重放。
2条转发通道
12项规则能力
200条规则上限
0字节外传

它消掉了哪一类成本

跨环境调试通常要付出三种成本之一:改后端、每个项目改一次配置,或者本地伪造一套构建。这款扩展把这三者收敛成一条浏览器规则,并对你打开的每个页面生效。

以最常见的「UAT 上有修复、我的 FAT 页面没有」为例,前后对比
没有扩展时 有了一条规则后
改 devServer 代理表,然后重启开发服务——每个项目一次,同事还看不到。 添加 https://fat-api.example.com/*https://uat-api.example.com,立即对浏览器里的所有项目生效。
请后端把你的源加进 Access-Control-Allow-Origin 并重新发布。 带任一高级能力的请求由扩展应答,页面侧 CORS 校验不会触发。
在源码里硬写另一个环境的 token,还得记得改回去。 按规则注入请求头,调试完把规则关掉即可。
等一个还没写完的接口,或者在应用里塞桩代码。 在规则里 Mock——还能按 URL、方法、查询参数返回不同内容。
抓包工具里录下流量,再手工复现一次请求去验证异常分支。 导入 HAR 或粘贴 cURL 命令,规则自动预填。

一条规则能做到哪些事

匹配模式、目标地址与优先级是一条规则的骨架。其余能力都是可选且可叠加的——而你开启的每一项能力,同时也决定了这条规则走哪条通道。

两条通道,一套规则集

仅重写 URL 的规则会被编译为 declarativeNetRequest 动态重定向,由浏览器网络栈完成,单请求零 JavaScript 开销——对 XHR、页面跳转、内联框架、脚本、样式、图片、字体与媒体资源同样生效。更复杂的规则走后台服务线程:主世界拦截器接管 fetchXMLHttpRequestWebSocket,经隔离世界的内容脚本桥接,再把响应交回页面。

网络层零 JS 快通道主世界拦截单请求 30 秒上限失败回退原生

请求与响应的全链路改写

重写 URL;注入或替换请求头;替换请求体;改写响应状态码与响应头,或按点分路径(如 data.token)替换 JSON 字段。头名会做合法性校验、值里的 CRLF 会被直接拒收,规则无法构造请求拆分;Mock 与改写的状态码被钳制在 200–599,保证页面总能构造出合法响应。

HBRMDXReWS规则列表逐条能力徽标

通配符 / 前缀 / 正则三套匹配

三种匹配类型:* 匹配任意字符、前缀匹配开头、正则整体匹配且目标地址可引用捕获组。通配符与前缀在两条通道上的重写结果完全一致;正则只有覆盖整个 URL 时才一致——网络层整条替换 URL,后台通道只替换模式命中的那一段。用户正则会先做嵌套量词 ReDoS 筛查,送往网络层的正则还要通过 Chrome 的 RE2 校验。

支持条件化的 Mock 响应

不碰任何服务器,直接返回构造好的 JSON / 文本 / HTML / XML 与状态码;还可以挂多组条件(URL 正则、方法、查询参数),首个命中的条件决定响应内容。一条规则就能替整组接口,或同一接口的多种状态。

延迟、阻断与重试

注入 0–60000 毫秒延迟验证骨架屏与超时;把请求阻断成网络错误;或让扩展在一次失败或 5xx 之后追加 1–5 次尝试,间隔可在 100 至 30000 毫秒之间设置。

方法过滤与查询参数注入

把规则限定在 GET / POST / PUT / DELETE / PATCH / OPTIONS / HEAD 中的若干方法(例如只把写操作打到测试后端),或者不改写地址、只在最终 URL 上追加或覆盖查询参数,如 __env=uat 与灰度标识。

WebSocket 转发

通过重写 socket 地址把 ws://wss:// 连接指向另一个环境,让实时业务跟 REST 一起切换环境。匹配模式可以用 http(s) 也可以写 ws(s),方法过滤把握手视为 GET

是一个规则工作台,不是一份配置文件

规则列表为空时可从快速模板起步,复制规则、拖拽调整优先级、批量启停与删除,删除后还可撤销。关键词可搜名称、匹配模式与目标地址,再配合状态与匹配类型下拉进一步收窄列表。当同模式的更高优先级规则会把当前规则遮蔽时,编辑时会给出冲突提示,避免写下一条永远不会命中的规则。而浏览器网络层根本不会应用的规则——RE2 不支持的正则语法、越界的捕获引用——会在列表里直接标为「未生效」。

用证据说话,不靠猜

URL 匹配测试器可实时预览任意 URL(可带方法)的命中规则、重写结果、转发通道、额外动作,以及同样命中但被它遮蔽的规则。请求日志保留最近 500 条,含方法、状态、耗时与命中的规则,可按方法、状态区间、规则名或 URL 关键词筛选,详情可看请求与响应的头与文本 body(二进制响应体不落盘),可按原始请求地址复制为 cURL,并展示两条通道各自的命中数。

导入导出与环境快照

JSON 配置导出,默认开启分享模式,自动剔除 Authorization、Cookie 等请求头/响应头与 token 类查询参数,取消勾选即原样备份;导入时可选择覆盖或与现有规则合并;HAR 1.2 导出捕获到的流量,导入 HAR 则把录制到的请求自动变成规则(新规则默认停用,确认后自行启用);粘贴 cURL 预填表单;每条日志都能按原始请求地址复制为 cURL。环境快照保存整套规则的命名副本,FAT / UAT / PROD 一键切换;环境换域名时,批量迁移会先给出变更预览。

每天真正要碰的部分

弹窗里集中了总开关、启用规则数与今日请求数(只统计后台通道)、最近请求、规则快捷启停、自动关闭倒计时,以及当前页命中预览和「为此页面创建规则」。工具栏角标显示代理状态;自动关闭(30 分钟至 4 小时,基于 chrome.alarms)会到点关掉总开关,忘了关的代理不会活过调试本身。

界面细节

中英文界面、6 套主色配浅色/深色/跟随系统,弹窗、配置页与页面内组件同步切换;键盘快捷键:⌘⇧P 开关代理、N 新建规则、/ 聚焦搜索、Esc 关闭。单键快捷键是有意为之——⌘/Ctrl+N 会被浏览器保留用于新建窗口,页面捕获不到。

界面预览

以下均为当前版本的真实截图。配置页承载规则表、匹配测试、请求日志与导入导出;弹窗把总开关与当前页预览放在一步可达的位置。

工作原理

每条启用的规则在每次配置同步时从存储里重新分类。是这个分类决定请求由浏览器网络栈还是扩展后台应答,匹配测试器展示给你的也就是它的结果。总开关管着两条通道:关掉时网络层规则被整体清空,后台通道也原样放行请求。

页面发起请求fetch · XMLHttpRequest · WebSocket
命中一条启用中的规则优先级数值小的先命中;都没命中则原样放行
按这条规则的能力分流分类在每次配置同步时重新判定,不是按请求临时猜
通道 A · 浏览器网络层
简单规则只做 URL 重写,不含下方任何一项
declarativeNetRequest 重定向由浏览器网络栈改写地址 · 单请求零 JS 开销
目标环境直接应答仍是一次跨域请求,同源策略照常生效(见下方 CORS 说明)
通道 B · 扩展后台
复杂规则改写 · Mock · 延迟 · 阻断 · 重试 · WebSocket · 方法过滤
页面拦截器(主世界)接管 fetch / XHR / WebSocket · postMessage 仅限本源
桥接(隔离世界)经 chrome.runtime 转发;非字符串请求体走原生路径
后台服务线程代发并回传持有主机权限 · 构造响应交回页面
两条通道由同一份规则集驱动,区别只在「谁来改写这个请求」。总开关关掉时,网络层规则被整体清空,后台通道也原样放行请求。

一旦包含下列任一项,规则就不再「简单」

切到后台通道的完整条件,以及背后的原因
能力 为什么网络层做不到
请求头 / 请求体覆盖 替换请求体需要重新发一次真实请求,而不是重定向。
响应状态码 / 响应头 / JSON 字段覆盖 响应需要先读出来、再重建。
Mock(含或不含条件)、延迟、阻断、重试 它们改变的是请求到底会不会发生、何时发生、发生几次。
HTTP 方法过滤 重定向规则无法按请求方法做条件。
查询参数注入 在重写之后才应用,重定向替换无法表达。
WebSocket(ws:// / wss:// declarativeNetRequest 没有 WebSocket 资源类型。
不以 * 结尾的通配符,或目标地址为空 强行替换会静默丢弃尾部固定文本,或产生非法的重定向值。

关于 CORS,说得准确一点

后台通道的请求由持有主机权限的扩展发出,页面拿到的是扩展构造的响应,因此不会触发页面侧 CORS 校验。仅重写 URL 的请求仍是一次普通的跨域请求:浏览器对重定向后的响应套用同源策略,依然要求 Access-Control-Allow-Origin 覆盖你的源。若目标环境不允许你的源,给规则加上任一能力(加一个响应头覆盖代价最小),它就换到后台通道。

拦截失败时,页面会回退到原生的 fetchXMLHttpRequestWebSocket;FormData、Blob、ArrayBuffer 等非字符串请求体也走原生路径,而不是静默丢体。阻断规则是刻意保留的例外:被阻断的请求绝不回退重放,否则发出去的就是你要压掉的那个请求。

chrome.storage.local 是唯一事实源。后台服务线程内的缓存——编译好的正则、规则列表、命中计数——均可重建,并在每次配置变更时失效,因此服务工作线程重启后扩展行为依旧正确。

四步开始使用

无需注册、无需服务器、无需安装证书。从克隆到第一条代理请求大约一分钟。

01

构建

需要 Node.js 20+ 与 pnpm 10。不想装工具链?每个打 tag 的版本都会在 Releases 附上预构建 zip,解压即可——先确认那里已有发布记录。

git clone https://github.com/liaolongdong/cross-origin-proxy
cd cross-origin-proxy
pnpm install
pnpm build
02

加载

打开 chrome://extensions,开启右上角「开发者模式」,点击「加载已解压的扩展程序」并选择 .output/chrome-mv3(或刚从 Releases 解压出来的目录),建议把图标固定到工具栏。

03

添加一条规则

  1. 在弹窗中打开代理总开关。
  2. 通配符规则:https://fat-api.example.com/*https://uat-api.example.com
  3. 设定优先级(数值小者优先)并保存。
04

验证

刷新页面。像示例里这种通配符重写发生在浏览器网络层,不会产生逐条请求日志:请在 URL 匹配测试器里预览这条地址,并看规则的 DNR 命中数。只有走后台通道的请求才会出现在请求日志里。

更新解压版构建时:请覆盖原目录里的文件。换一个目录加载会被 Chrome 视为一次全新安装并分配到不同的扩展 ID,而规则、日志与环境快照在 chrome.storage.local 中按 ID 隔离存放——旧规则看起来就空了。

使用场景与对应的规则写法

从场景到配置的最短路径。下表每一行都是扩展已实现的行为,不是变通方案。

常见场景与各自用到的能力
场景 规则配置 通道
在 FAT 页面上验证只有 UAT 才有的修复 把 API 前缀通配符重写到 UAT 域名。 DNR
后端接口还没写,先把 UI 做出来 Mock 响应自定内容与状态码;用条件让不同接口返回不同内容。 SW
验证加载态与超时分支 对具体接口注入 3000–60000 毫秒延迟。 SW
验证离线兜底与 500 错误页 阻断请求,或改写响应状态码。 SW
上游不稳定,希望读接口更韧性 重试 2–5 次,间隔 1000 毫秒。 SW
命中灰度 / A/B 分支 查询参数注入,如 __env=uat SW
只把写操作打到测试后端 方法过滤只允许 POST / PUT / DELETE SW
在另一个环境调试实时功能 把页面的 socket 地址重写到目标 socket。 SW
页面因为某个字段缺失而报错 按点分路径替换 JSON 字段,如 data.token"mock-token" SW
目标环境不允许你的源 加上任一能力(如响应头覆盖)即可离开网络层。 SW
把同一套配置交给同事 导出 JSON(分享模式默认剔除凭据),或分享由 HAR 衍生的规则集。
环境换了新域名 批量迁移目标地址,并先查看变更预览。

与其他方案对比

表头是工具类别而非具体产品:同一类里的能力差异很大,请以你在用的工具实际文档为准。

跨环境调试与请求改写的常见路线。颜色按你的收益读:绿色为该行有利,橙色为部分支持,灰色为不具备。
方案 本扩展 开发服务器代理 抓包代理工具 API 客户端 / 改头类扩展 改应用配置
需要后端或网关配合改动 不需要 为 CORS 常常需要 不需要 不需要 不需要
需要逐个项目配置 否——全浏览器共用 系统级配置 按规则集
对任意页面源生效(localhost、内网系统) 仅指向该开发服务的流量 受扩展 API 限制
改写响应(状态码、响应头、JSON 字段) 支持 不支持 支持 响应体通常不行 不支持
界面里直接 Mock / 延迟 / 阻断 / 重试 支持,含条件化 Mock 需额外插件 支持 通常只有 Mock 不支持
覆盖 WebSocket 流量 支持 较少见 支持 不支持 不支持
读 HTTPS 需要本地 CA 证书 不需要——在浏览器内运行 不需要 需要 不需要 不需要
规则可用普通本地文件分享 JSON / HAR / cURL 随仓库代码共享 会话文件 常需云端同步 随仓库代码共享
能代理服务端到服务端等非浏览器流量 不能——仅浏览器范围 不能 不能 看情况

代价说在明处:它是一个浏览器内的工具。它无法帮助服务端之间的调用,不会把任何内容录到云端工作区,规则也存在你的 Chrome 配置文件里而不是共享的项目文件中——想给同事一份,就导出 JSON。抓包代理工具适合系统级流量,而本扩展适合「这个页面应该去连那个环境」。

这个对比的完整版——每类方案真正擅长什么,以及六种不该用本扩展的情形

运行要求与开发命令

运行环境

较新版本的桌面版 Google Chrome(Manifest V3)。推荐通过 Chrome Web Store 安装;也可以从源码构建并加载 .output/chrome-mv3,或加载从 Releases 下载的预构建 zip。Edge 支持 Chromium 扩展,同一构建通常可用;Firefox 因为 declarativeNetRequest 差异目前不是支持目标。与其他扩展一样,chrome:// 页面、应用商店与其他扩展页面无法注入。

构建环境

Node.js 20 及以上、pnpm 10(见 packageManager)。技术栈为 WXT + Vue 3 + TypeScript + Element Plus + Vite,Element Plus 按组件引入以控制包体;匹配、重写与导入导出逻辑由 Vitest 覆盖。

开发命令

pnpm dev          # WXT 开发服务,带热更新(端口 8899)
pnpm build        # 生产构建 → .output/chrome-mv3
pnpm build:zip    # 商店上传用 zip
pnpm test         # vitest 单元测试
pnpm typecheck    # tsc --noEmit
pnpm lint         # eslint
pnpm lint:style   # stylelint
pnpm assets       # 重新生成商店与落地页图片

隐私与权限

所有内容都存在本机的 chrome.storage.local。没有账号、没有统计埋点、没有遥测、没有自有远程服务:唯一的网络流量就是你让它代理的 API 流量。日志与导出均在本地生成——也需要提醒一句:日志会记录被代理的请求内容(请求头可能含 token),分享导出的 HAR 或 cURL 前请先检查。阅读隐私政策

清单声明的每一项权限,以及它的用途
权限 为什么需要
storage 持久保存规则、请求日志、环境快照与偏好设置。
declarativeNetRequest 安装网络层重定向规则,让简单改写做到单请求零 JS。
declarativeNetRequestFeedback 读取哪些重定向规则真正命中,用于日志抽屉里的命中统计。
alarms 代理期间维持后台服务工作线程,并运行自动关闭倒计时。
<all_urls> 主机权限 代理必须在你前端运行的任意源上生效;目标环境是开发者自己的内网域名,无法提前枚举。

代码里硬性限制的上限

规划规则集时可以直接依赖的数值
约束 取值
规则数 新增、批量新增与合并导入时最多 200 条;替换式导入按文件内容原样写入
请求日志 最近 500 条(环形缓冲)
请求体 最大 10 MB
注入延迟 0 – 60000 毫秒
重试 0 – 5 次,间隔可配
单请求超时 30 秒
Mock / 改写的状态码 钳制在 200 – 599
自动关闭 30 分钟 / 1 小时 / 2 小时 / 4 小时

常见问题

页面内容也是机器可读的:同样的问答以 FAQPage 结构化数据发布,llms.txt 则向 AI 助手概述这款产品的能力与架构。

基础与匹配

浏览器的跨域代理到底是做什么的?

它拦截页面发出的 API 请求,并把请求送到别处。在研发场景里,通常就是让一个连着某个后端的前端页面改连另一个环境,于是不改应用代码、不重新部署,也能在 FAT 页面上验证只有 UAT 才有的修复。

它能拦截哪些请求?

页面主世界的拦截器会接管 fetchXMLHttpRequestWebSocket,相对地址在匹配前先解析为绝对地址。仅重写 URL 的规则会被编译成 declarativeNetRequest 重定向,由浏览器对匹配的各类资源生效:主文档与子框架、XHR、脚本、样式、图片、字体、媒体与其他。

通配符、前缀、正则三种匹配方式有什么区别?

通配符把 * 视为任意字符,并把结尾 * 捕获的内容拼接到目标地址之后;前缀匹配 URL 开头,把剩余部分拼到目标之后;正则要覆盖整个 URL 才能让两条通道结果一致——后台通道只替换模式命中的那一段,网络层则整条替换,因此建议用 ^ 锚定并把结尾写成 (.*)$。多条规则同时命中时,优先级数值小者优先,数值相同则按规则列表里的先后顺序,请给每条规则不同的数值。

所有网站都能用吗?

内容脚本注入到所有 httphttps 页面,规则按请求 URL 匹配,因此内网系统、localhost 开发服务、预发域名都适用。chrome:// 页面、Chrome 应用商店与其他扩展页面被浏览器统一限制,任何扩展都无法注入。

通道、CORS 与性能

它能绕过 CORS 限制吗?

使用了任一高级能力(请求头覆盖、请求体覆盖、响应覆盖、Mock、延迟、阻断、重试、方法过滤、查询参数注入、WebSocket)的规则,由持有主机权限的扩展后台上下文发出请求,页面拿到的是扩展构造的响应,因此不会触发页面侧的 CORS 校验。仅重写 URL 的规则走网络层重定向,浏览器仍会校验重定向后响应的 Access-Control-Allow-Origin。若目标环境不允许你的源,给规则加上任一高级能力(加一个响应头覆盖代价最小)即可切到后台通道。

为什么某条规则实际走的通道和我预期的不一样?

因为网络层表达不了你想要的行为。不以 * 结尾的通配符、目标地址为空,或者带了任一高级能力,都会被刻意路由到后台通道——若强行编译成重定向,结果会被悄悄改变。URL 匹配测试器会在你依赖某条规则之前,把任意 URL 的转发通道显示出来。

它会不会拖慢页面?

只重写 URL 的规则单请求零 JavaScript 开销,由浏览器网络栈直接完成重定向。走后台通道的请求会多一次扩展内的中转,外加你配置的延迟,单个代理请求有 30 秒上限。用户编写的正则会先做嵌套量词 ReDoS 检测,送往网络层的正则还需通过 Chrome 的 RE2 校验——一条不支持的正则会让整批规则安装失败,因此在同步前就被过滤掉。

为什么简单规则没有逐条请求日志?

网络层重定向不会经过扩展,没有可记录的逐条请求。规则级命中数会出现在「DNR 命中统计」里,统计窗口为最近 5 分钟;Chrome 对该读取接口有配额限制,因此面板改为手动刷新,超出配额时静默保留上一次结果。后台通道的命中数单独统计。

Mock 与故障注入

Mock 除了返回固定内容还能做什么?

一条规则可以挂多组条件——URL 正则、HTTP 方法、查询参数——首个命中的条件决定响应体、状态码与 Content-Type,全部不命中时回落到规则的默认响应体。于是一条规则就能替多个接口、或同一接口的多种状态兜底。Mock 请求完全不触网,返回状态码被钳制在 200–599。

阻断请求和强行返回 500 有什么区别?

阻断让请求以网络故障的方式失败,用来验证离线兜底与重试逻辑;改写响应状态码则给你一个真实的 HTTP 错误码、状态文本与响应头,用来验证依赖状态码的错误渲染。被阻断的请求不会被原生回退重放——否则发出去的正是你要压掉的那个请求。

失败重试是怎么工作的?

重试是规则级开关,运行在后台通道。开启后,规则在首次请求之外追加 1 至 5 次尝试,间隔可在 100 至 30000 毫秒之间设置(默认 1000 毫秒)。某次尝试抛出异常(包括触及单请求 30 秒上限被中止)、或返回 5xx 且仍有剩余次数时触发重试;次数用尽后把最后一次响应或错误交回页面。因为重试属于后台通道能力,启用重试的规则永远不会被判定为网络层重定向。

分享、限制与安全兜底

规则可以分享给团队吗?

可以。导出为 JSON 配置,默认开启分享模式——自动剔除 Authorization、Cookie 等请求头与响应头、以及 token 类查询参数,需要本机全量备份时取消勾选即可原样导出;同事导入时可选择覆盖,也可与现有规则合并;可把捕获到的流量导出为 HAR 1.2,也可导入别处抓的 HAR 自动生成规则;粘贴一条 cURL 命令(DevTools「复制为 cURL」产物)即可预填规则表单。环境配置快照会保存整套规则的命名副本。

有哪些限制?

最多 200 条规则,最近 500 条请求日志,请求体上限 10 MB,延迟 0 至 60000 毫秒,重试默认关闭、开启后追加 1 至 5 次尝试且间隔 100 至 30000 毫秒,单个代理请求 30 秒上限,Mock 与改写的状态码钳制在 200–599,以保证页面总能构造出合法的 Response 对象。

扩展处理失败时会怎样?

页面会回退到浏览器原生的 fetchXMLHttpRequestWebSocket,请求照常发出,表现如同规则不存在。FormData、Blob、ArrayBuffer 等非字符串请求体无法跨消息通道传递,同样回退原生。唯一的例外是阻断规则:被阻断的请求绝不会被原生回退重放。

隐私与兼容性

我的数据会被上传到服务器吗?

不会。规则、日志、环境快照与偏好设置都保存在本机的 chrome.storage.local 里。扩展不含统计埋点、不上传任何数据,也没有自有远程服务,唯一的网络流量就是你让它代理的 API 流量。需要留意的是,请求日志会在本机记录被代理的请求头与请求体(可能含 token),在分享导出的 HAR 或复制的 cURL 命令之前请先检查。

支持 Firefox 或 Edge 吗?

面向 Chrome(Manifest V3)构建与验证。Edge 支持 Chromium 扩展,同一份构建通常可用;Firefox 的 Manifest V3 在 declarativeNetRequest 上存在差异,目前不是支持目标。

作者的其他插件

同一位作者、同一套做法:Manifest V3、开源、数据只留在本机、不连任何自有服务。联调时它们常常一起开着。

TF

Transfer Any File

浏览器内格式互转

14 种格式在浏览器里互转,一个字节也不上传:Markdown、Word、PDF、Excel、CSV、JSON、HTML 与图片在你自己的电脑上完成转换,支持批量混合格式、自动多步链路、预览与内联编辑、ZIP 打包。无账号、无上传、无网络请求。