使用文档

最后更新 2026-09-18

本页是 https://corx.envx.cn 实例的说明书:四种调用方式、全部控制参数、鉴权层级、缓存行为、会遇到的限额,以及背后的安全模型。自托管自己的一份,以仓库里的 README.md 为准。

调用代理

下面四种写法最终都会走同一条链路:鉴权、SSRF 防护、上游注入、缓存、日志。CORS 预检(`OPTIONS`)在进入代理前就已应答,浏览器的 `fetch` 可以直接用。

查询参数(推荐)

本文档统一使用的写法:目标 URL 经过百分号编码后放进 `?url=`。`/fetch` 和任何其他代理路由都支持。

fetch("https://corx.envx.cn/fetch?url=" + encodeURIComponent("https://api.example.com/data"))

路径式

把目标接在 `/proxy/` 之后,便于阅读,也能直接粘进浏览器;目标自己的查询串在第一个 `?` 之后完整保留。

fetch("https://corx.envx.cn/proxy/https://api.example.com/data")

裸路径

与 `/proxy/` 相同,只是少一段:任何不是 CORX 页面、又看起来像 URL 的路径都会走代理。

fetch("https://corx.envx.cn/https://api.example.com/data")

子域名模式

部署绑定了泛解析域名时,每个目标可以拥有自己的主机名:点变连字符、连字符翻倍(`api.example.com` → `api-example-com.<zone>`)。该请求的查询串就是目标的查询串,因此 `corx-*` 名字会被剔除。

fetch("https://api-example-com.<zone>/data")

GET 和 HEAD 响应会被缓存并计入配额,其他方法一律直接透传、不缓存。调用方自带的目标会完整保留自己的查询串:目标自己的 `key`、`ttl`、`callback` 参数原样转发,CORX 只消费属于自己的名字。

fetch、axios、ky 以及 Cloudflare Pages、Vercel、Netlify 的可复制示例,都在代码示例页。或者用 CORS 测试器直接在浏览器里测试任意 URL。

corx-* 命名空间

`corx-*` 是 CORX 的命名空间:这些参数由代理消费,绝不会转发给目标。其余参数都归目标所有,原样转发。表中没有的 `corx-*` 名字会直接 400,而不会被悄悄转发。

参数作用
corx-ttl
corx-ttl=300
本次 GET 响应的缓存 TTL(秒)。会被部署上限(以及公共 key 自身的 TTL)压低,避免调用方把条目钉死一整天。
corx-no-cache
corx-no-cache=1
本次请求跳过 R2 缓存:直接回源、返回,且不写入缓存。Range 请求、JSONP 和携带凭证的请求本来就会绕过缓存。
corx-key
corx-key=corx_…
本次请求使用的 API key,等价于 `X-Api-Key` 或 `Authorization: Bearer`。公共档位不能控制缓存。
corx-callback
corx-callback=handleData
JSONP:当 CSP 拦住 `fetch` 时,把 `application/json` 响应包成 `fn(<json>);`(上限 2 MiB)供 `<script>` 使用。JSONP 永不缓存。
corx-charset
corx-charset=utf-8
用指定编码重新解码文本、JSON 或 XML 响应,并以 UTF-8 重新输出——上游把编码标错时的救命参数。未知编码名返回 400。
corx-wrap
corx-wrap=json
把文本响应包成 `{"contents":"…"}`(`application/json`),让 HTML 也能用 `r.json()`。二进制响应会直接 400;包装方式属于缓存键的一部分。对无正文状态(`204`、`304` 等)两个转换参数都不生效:状态码与校验头原样透传。
corx-scheme
corx-scheme=http
子域名模式:目标协议。默认 `https`,另一个可选值是 `http`。
corx-port
corx-port=8443
子域名模式:目标端口(1–65535),若不是该协议的默认端口(http 为 80、https 为 443)则拼接在主机名之后。

子域名和路径式调用里,代理请求的查询串同时也是目标的查询串,因此 `corx-*` 名字会被剔除。如果目标确实需要一个叫 `corx-*` 的参数,请改用 `?url=`。

鉴权

三种进入方式,大致就是自托管部署逐步启用的顺序。

API key(按调用方)

在控制台创建的 key 形如 `corx_<随机串>`,库里只存 SHA-256 哈希,原始值只展示一次。它可以携带允许来源、每分钟频率限制、缓存 TTL、免密钥授权、允许主机、SSRF 检查开关和上游注入。用下面任意一种形式发送即可。

X-Api-Key: corx_…
Authorization: Bearer corx_…
?corx-key=corx_…

三种形式完全等价,用你手头客户端支持的那种即可。CORX 认证所用的凭证绝不会转发到上游:`corx_` 形态的 `X-Api-Key` 或 `Authorization: Bearer corx_…` 一律被消费,即使 key 无效也不例外。`X-Api-Key` 里的其他值则是你自己的上游凭证(BYOK),原样转发——请同时用 `Authorization: Bearer corx_…` 或 `?corx-key=` 携带 CORX key(`corx_` 形态的 `X-Api-Key` 优先)。

免密钥来源授权

给 key 开启免密钥(keyless)后,其允许来源的浏览器调用代理时完全无需携带 key。授权按 `Origin` 匹配(同源 GET 时回退到 `Referer` 的来源),并按访客 IP 计量,因此单个嵌入站点无法耗尽整个 key。允许来源在保存时会规范化(主机小写、省略默认端口),唯一的通配形式是 loopback 端口——`http://localhost:*` 覆盖 localhost 的所有端口,随机端口的开发服务器只需一条。主机通配(`https://*.example.com`)和正则会被拒绝。来源是便利措施而非凭证——脚本可以伪造——请配合允许主机和频率限制使用。

公共档位

托管实例可能在落地页公开一个共享 key,它是刻意削减过的:仅 GET/HEAD、按调用站点/目标站点/整个实例的每日配额、不能控制缓存、不能注入、不支持子域名模式;转发前会剥掉 `Cookie` 和 `Authorization`。适合公开数据、演示和原型。

key 该放在哪,绝不能放在哪

只能放在服务端。出现在浏览器打包产物、公开仓库或页面源码里的 key,就等于已经泄露;需要从浏览器调用代理的站点应该使用免密钥来源授权(如果数据确实是公开的,就用公共 key)。无论如何,都不要让凭证或个人数据经过共享实例。

上游凭证

一个 key 可以保管应用需要的上游 API 凭证并在服务端注入:浏览器拿不到凭证值。规则按目标 host 决定发什么、发到哪;如果你把某个变量暴露出去,调用方自己也可以在请求里引用它。

凭证是怎么附上去的

变量(`NAME=value`)保存值,header 规则与 query 规则引用它们。`@host` 行给它下面的规则限定作用域,因此一个 key 可以为每个上游携带不同的凭证——包括最常见的形态:同一个 header 名在不同 host 上用不同的值(两个厂商都用 `Authorization`)。header 规则用于设置或删除请求头(`Authorization: Bearer ${VENDOR_KEY}`、`!X-Debug`);query 规则对应 URL 参数(`api_key = ${VENDOR_KEY}`),适用于用参数鉴权的 API。规则始终覆盖调用方发来的内容,浏览器无法伪造注入的 header;用哪条规则由目标 host 决定,而不是由调用方决定。

允许调用方引用变量

暴露是逐变量的——API 上是 `client: true`,控制台是 **客户端可引用变量** 字段——并在其中写明允许调用方把它发往哪些 host。调用方随后在自己请求的 header 或 query 里写 `${VENDOR_KEY}`,代理在转发前替换。调用方只知道它自己写的名字,永远拿不到值;你没有暴露的变量对它完全不可见。

两层作用域,且两层都必须允许该 host

规则的 `@hosts` 决定*这条规则*在哪里生效;变量自己的 hosts 决定*这个变量*最多能解析到哪里。后者约束前者:任何可能把被作用域限制的变量解析到别的 host 的规则,保存时就会被拒绝——包括 key 级规则,因为它能覆盖所有允许主机。两处的 pattern 语法一致:精确 host、`*.suffix`(标签边界)或 `*`;`*.vendor.com` 覆盖 `api.vendor.com`,而两者都不覆盖 `vendor.com` 本身。客户端引用按每一次重定向跳求值,所以作用域只到某个 host 的变量,在跳到别的 host 之后不会解析。

顺序,以及不匹配时会发生什么

转发时先解析调用方引用,再施加该 key 的规则(先 remove 后 set)。因此规则永远赢得过调用方——`!X-Debug` 会删掉它,`set` 会覆盖它——浏览器无法伪造注入的 header。代理不解析的内容(未知名字、私有变量、作用域外的 host)一律原样转发。这既保证功能是纯增量的(没有暴露任何变量的 key 行为与从前完全一致),也让「未暴露」和「不存在」表现一致,调用方无法探测 key 里有什么。代理自己拥有的 header——`corx_` 形态的 `X-Api-Key`、`X-Admin-Token` 以及 hop-by-hop 那一组——在处理任何引用之前就被丢弃;非 `corx_` 形态的 `X-Api-Key`(包括 `${VAR}` 引用)是调用方自己的上游凭证,会原样转发。所以需要这两个名字的厂商(如 Anthropic 的 `x-api-key`)两条路都通:可信服务端 BYOK 直通,或浏览器永不持有密钥时的规则注入。

一个 key,对接三个上游

控制台的 key 页面把变量做成逐行编辑(每行带“客户端”开关与主机作用域),旁边是 Header / Query / Response 规则字段和允许主机字段;Admin API 对应 `vars`(每个变量可带 `client` 与 `hosts`)、`headerRules`、`paramRules`、`responseRules`、`allowedHosts`。

变量(Variables)

OPENAI_KEY=sk-…
ANTHROPIC_KEY=sk-ant-…
VENDOR_KEY=vendor-…

客户端可引用变量(Client-referencable variables)

@api.vendor.com
VENDOR_KEY

Header 规则(Header rules)

@api.openai.com
Authorization: Bearer ${OPENAI_KEY}
@api.anthropic.com
x-api-key: ${ANTHROPIC_KEY}
@api.vendor.com
Authorization: Bearer ${VENDOR_KEY}

允许目标主机(配置注入时必填)

api.openai.com, api.anthropic.com, api.vendor.com

每条 `@host` 都必须落在允许主机列表内,而只要配置了注入,该列表就是必填的——这道守卫防止调用方把你的凭证指向它自己选的 host。不在列表内的规则永远不会被附加。

调用方的引用最终变成什么

以上面的配置为例:`VENDOR_KEY` 只对 `api.vendor.com` 暴露,`PRIVATE_KEY` 未暴露,并且有一条针对 `api.vendor.com` 设置 `Authorization` 的 header 规则。

调用方发送目标 host上游收到
x-vendor: Bearer ${VENDOR_KEY}
已暴露,且该 host 在变量作用域内 → 解析。
api.vendor.comx-vendor: Bearer vendor-…
x-vendor: Bearer ${VENDOR_KEY}
在 key 的白名单内,但在变量作用域之外 → 原样保留。
api.other.comx-vendor: Bearer ${VENDOR_KEY}
x-private: ${PRIVATE_KEY}
未对调用方暴露 → 原样保留。
api.vendor.comx-private: ${PRIVATE_KEY}
x-typo: ${NOPE}
不存在该变量 → 原样保留,与私有变量表现一致。
api.vendor.comx-typo: ${NOPE}
Authorization: Bearer ${VENDOR_KEY}
规则随后生效 → 以规则值为准,覆盖调用方的引用。
api.vendor.comAuthorization: Bearer <what the rule sets>

用途

最常见的是同一个前端调用多个服务——一家厂商的 LLM、另外几家的支付或地图 API——各自一个 key,页面里没有任何密钥。它同样适用于用查询参数而非 header 鉴权的 API,以及同一上游在不同环境用不同 key 的情况(每个环境一个 key,或用变量保存该部署的值)。配合客户端引用,页面甚至可以在多个凭证之间选择用哪一个,而始终不持有任何一个——这是浏览器端应用对接多家厂商后需要的模式。

一个 key 还是多个

以上做法把所有上游放进同一个 key,也就意味着它们共享一份允许主机并集、一份频率限制和一份允许来源列表,以及一个需要轮换的密钥库。当你需要各自独立的限额或来源、希望泄漏时只波及一家厂商、或同一个 host 的不同路径需要不同凭证(规则按 host 而不是按路径限定作用域)时,就拆成每个上游一个 key。免密钥访问只给一个来源授权一个 key,所以不带 key 调用多个上游的页面,拿到的就是那一个 key 的规则——而你暴露出的那些变量,它也可以引用。

缓存与安全

设置 `INJECTION_KEK` 后密钥静态加密,在所有读取路径(控制台、日志、playground 预览)都以掩码显示,请求日志记录的是注入前的 URL。带 header 规则的 key 完全不读写 R2 缓存,因为它的响应与 key 相关;只用 query 参数注入的 key 仍然缓存,按生效后的 URL 作为缓存键。而只要 key 里有任何客户端可引用变量,就完全不使用共享缓存:引用由调用方提供,且重定向可能在后续跳才解析,因此经这个 key 的任何响应都无法证明不含密钥。公共档位完全不能注入——它只支持 GET/HEAD——所以上游凭证意味着自托管实例加 standard key。被暴露的变量等同于「谁能调用这个 key,谁就能引用它」,请谨慎放开。

缓存

GET 响应会缓存在 R2 并从边缘返回,重复请求通常根本到不了上游。每个响应上的 `X-Corx-Cache: HIT|MISS` 会告诉你走了哪条路。

缓存标记

调试时最该盯的就是 `X-Corx-Cache`:`MISS` 表示响应来自上游(可缓存时才会写入),`HIT` 表示由 R2 直接返回。`X-Corx-Target` 给出上游主机名,`X-Corx-Latency-Ms` 是代理消耗的时间。

TTL

默认使用部署的 TTL,除非用 `?corx-ttl=` 调低或调高(不超过上限)。key 也可以设定自己的默认 TTL,或设成 `0` 表示永不写入;公共档位完全不能设置 TTL。

哪些请求绕过缓存

以下情况一定绕过共享缓存:非 GET/HEAD 方法、携带 `Authorization` 或 `Cookie`、`?corx-no-cache=1`、JSONP(`corx-callback`)、Range 请求,以及会注入上游请求头的 key。带响应头规则的 key 仍然会缓存——其解析后的规则属于缓存键的一部分,改写过的响应绝不会返回给另一个 key。

限额与错误

有两层彼此独立的限制在保护实例:按 key(匿名时按 IP)的每分钟频率限制,以及公共档位的每日配额。

每分钟限制按 key 计数,匿名调用按 IP 计数;缓存命中不计入——只有未命中才计量。底层 D1 故障时这些检查会放行(fail open),以免代理整体不可用。

公共 key 的每日计数按调用站点、目标站点和整个实例三个维度,以 UTC 自然日为单位。命中缓存也计入——配额算的是请求数,不是上游压力。`X-Corx-Quota-{Origin,Host,Day}-{Limit,Remaining}` 会报告你的剩余额度。

触碰公共档位每日配额时,代理返回 `429`,带 `Retry-After`(距 UTC 零点重置的秒数)和 `{ error, scope, limit, resetAt }` 的 JSON body;每分钟限流的 429 则只是 `{ error }`,没有重试提示。普通响应也会带 `X-RateLimit-Limit` 和 `X-RateLimit-Remaining`。所有面向机器的路径(代理、`/api/*`、`/health`)出错都是 JSON `{ error }`;浏览器页面则是带品牌的 HTML 错误文档。

安全

CORX 是 CORS 代理,因此本质上就是中间人:运行实例的人可以读取、修改并重放经过它的所有内容。下面的防护只能限制不可信调用方能碰到什么,并不能让共享实例变得适合承载机密。

SSRF 防护:私有、链路本地、CGNAT、组播和保留网段的 IP 字面量会被拦截;主机名会通过 DoH 解析并复核,防止重绑定到内网;D1 黑名单按域名及其子域生效。可信的 key 可以关掉 IP/主机名检查和 DNS 检查,但黑名单与 Cloudflare 自身的规则永远不会被绕过。

请求头卫生:逐跳头和代理自有头(`Host`、`Connection`、`X-Forwarded-For`、`CF-*` 等)在进出两个方向都会被剥掉,`Set-Cookie` 不会转发,公共档位在转发前剥离 `Cookie` 和 `Authorization`。

代理能看到什么:目标 URL、请求与响应体,以及调用方的 IP、`Origin` 与国家/地区。托管实例会记录请求并汇总成每日聚合;自托管可以自行决定是否记录、保留多久(`LOG_REQUESTS`、`LOG_RETENTION_DAYS`)。

所以运行 CORX 最诚实的方式就是自托管:一个 MIT 许可的 Worker,放在你自己掌控的账号里。托管实例只是共享的尽力而为演示——在把流量交给它之前,请先读一读使用条款和信任模型。

使用条款 · 信任模型

自托管

本页上的每一项限制——配额、频率、保留时长、允许主机——在自托管之后都是你自己 Worker 上的设置。README 覆盖了约十分钟的部署流程(D1 + R2 + `wrangler deploy`);CONTRIBUTING.md 写明了本地开发和改动必须通过的检查。

部署指南贡献指南