Usage

Last updated 2026-09-17

This page is the manual for the instance at https://corx.envx.cn: the four ways to call the proxy, every control parameter, the auth tiers, what the cache does, the limits you will hit and the security model behind it. README.md in the repository stays the source of truth for deploying your own copy.

Calling the proxy

Every shape below resolves to the same request through the same pipeline — auth, SSRF guards, upstream injection, cache, logging. CORS preflight (`OPTIONS`) is answered before the proxy runs, so browser `fetch` just works.

Query parameter (recommended)

The shape this documentation uses everywhere: the target URL, percent-encoded, in `?url=`. Works on `/fetch` and on every other proxy route.

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

Path

The target appended after `/proxy/`. Easy to read and to paste into a browser; the target's own query string survives after the first `?`.

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

Bare path

The same as `/proxy/`, one segment shorter: any path that is not a CORX page and looks like a URL is proxied.

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

Subdomain mode

When the deployment has a wildcard zone, a target gets a hostname of its own: dots become hyphens and hyphens double (`api.example.com` → `api-example-com.<zone>`). The request's query string is the target's query string, so the `corx-*` names are stripped back off.

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

GET and HEAD responses are cached and counted; every other method passes straight through, uncached. A caller-supplied target keeps its own query string: the target's own `key`, `ttl` or `callback` parameters are forwarded untouched, and CORX only consumes names it owns.

Copy-paste examples for fetch, axios and ky — and for Cloudflare Pages, Vercel and Netlify — live on the snippets page.Or test a URL's CORS from your browser with the CORS tester.

The corx-* namespace

`corx-*` is CORX's namespace: these parameters are consumed by the proxy and never reach the target. Everything else belongs to the target and is forwarded untouched. A `corx-*` name that is not in this table is a 400, never a param quietly forwarded upstream.

ParameterEffect
corx-ttl
corx-ttl=300
Cache TTL in seconds for this GET response. Capped by the deployment's maximum (and by a public key's own TTL) so no caller can pin an entry for a day.
corx-no-cache
corx-no-cache=1
Bypass the R2 cache for this request: fetch upstream, return, do not store. Range requests, JSONP and credentialed requests bypass it anyway.
corx-key
corx-key=corx_…
The API key for this request. Equivalent to `X-Api-Key` or `Authorization: Bearer`. Public-tier keys cannot control the cache.
corx-callback
corx-callback=handleData
JSONP: wrap an `application/json` body as `fn(<json>);` (max 2 MiB) for a `<script>` tag when CSP blocks `fetch`. JSONP never caches.
corx-charset
corx-charset=utf-8
Re-decode a text, JSON or XML response with this label and re-emit it as UTF-8 — the fix when an upstream mislabels its charset. An unknown label is a 400.
corx-wrap
corx-wrap=json
Wrap a text body as `{"contents":"…"}` with `application/json`, so `r.json()` works for HTML too. Binary responses are refused with a 400; the wrapper is part of the cache key.
corx-scheme
corx-scheme=http
Subdomain mode: the target scheme. Defaults to `https`; `http` is the only other accepted value.
corx-port
corx-port=8443
Subdomain mode: the target port (1–65535), appended unless it is the scheme's default (80 for http, 443 for https).

Subdomain mode is the one place where the proxy request's query is also the target's, so the control names are stripped back off there. A target that genuinely needs a `corx-*` parameter is best addressed with `?url=` or path mode.

Authentication

Three ways in — roughly the order a self-hosted deployment turns them on.

API key (per caller)

Created in the console as `corx_<random>` and stored only as a SHA-256 hash — the raw value is shown once. A key can carry allowed origins, a per-minute rate limit, a cache TTL, keyless grants, allowed hosts, SSRF-check opt-outs and upstream injection. Send it in any of the forms below.

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

All three are equivalent; use whichever survives your client or tooling.

Keyless origin grants

Enable keyless access on a key and browsers from its allowed origins call the proxy without carrying the key at all. The grant matches the `Origin` header (or the `Referer`'s origin for same-origin GETs) and is metered per visitor IP, so one embedded site cannot drain the key. An origin is a convenience, not a credential — scripts can forge it — so pair it with allowed hosts and a rate limit.

Public tier

A hosted instance may publish a shared key on its landing page. It is deliberately reduced: GET and HEAD only, daily quotas per calling site / per target host / per instance, no cache control, no injection and no subdomain mode; `Cookie` and `Authorization` are stripped before forwarding. Fine for public data, demos and prototypes.

Where the key goes — and where it must not

Server-side only. A key in a browser bundle, a public repository or a page source is a key you have given away; a site that needs to call the proxy from a browser should use a keyless origin grant (or the public key, if the data really is public). Never send credentials or personal data through a shared instance at all.

Caching

GET responses are cached in R2 and served from the edge, so a repeated request usually never reaches upstream. `X-Corx-Cache: HIT|MISS` on every response says which path it took.

Cache markers

`X-Corx-Cache` is the header to watch while debugging: `MISS` means upstream answered (and the response was stored), `HIT` means R2 answered. `X-Corx-Target` names the upstream host, and `X-Corx-Latency-Ms` is the time the proxy spent.

TTL

The deployment's default TTL applies unless `?corx-ttl=` lowers or raises it, up to the cap. A key can pin its own default TTL, or `0` to never store; the public tier cannot set a TTL at all.

What bypasses the cache

Always bypassing the shared cache: non-GET/HEAD methods, requests carrying `Authorization` or `Cookie`, `?corx-no-cache=1`, JSONP (`corx-callback`), Range requests, and keys that inject upstream headers. Keys with response header rules do cache — their resolved rules are part of the cache key, so a rewritten response is never served to another key.

Limits and errors

Two independent limits protect an instance: a per-minute rate limit per key (or per IP for anonymous calls), and the public tier's daily quotas.

The per-minute limit is counted on the key, or on the caller IP without one. Cache hits count too, and the D1-backed checks fail open during a database incident rather than taking the proxy down.

A public key's daily counters run per calling site, per target host and for the instance as a whole, in UTC days. Cache hits count as well — the quota is about requests, not upstream load. `X-Corx-Quota-{Origin,Host,Day}-{Limit,Remaining}` reports where you stand.

Over either limit the proxy answers `429` with a JSON body (`{ error, scope, limit, resetAt }`) and `Retry-After` — seconds until the window or the UTC day resets. Ordinary responses carry `X-RateLimit-Limit` and `X-RateLimit-Remaining`. Every machine-facing path (the proxy, `/api/*`, `/health`) answers errors as JSON `{ error }`; browser pages get a branded HTML document.

Security

CORX is a CORS proxy, so it is a man in the middle by construction: whoever operates an instance can read, change and replay everything passing through it. The guards below reduce what an untrusted caller can reach; they do not make a shared instance safe for secrets.

SSRF guards: private, link-local, CGNAT, multicast and reserved IP literals are blocked, the host is resolved over DoH and re-checked so a name cannot rebind to a private address, and a D1 blocklist covers whole hosts and their subdomains. A trusted key can opt out of the IP/hostname and DNS checks; the blocklist and Cloudflare's own rules are never bypassed.

Header hygiene: hop-by-hop and proxy-owned headers (`Host`, `Connection`, `X-Forwarded-For`, `CF-*`, …) are stripped on the way in and out, `Set-Cookie` is never forwarded, and the public tier strips `Cookie` and `Authorization` before forwarding.

What a proxy can see: the target URL, the request and response bodies, and the caller's IP, `Origin` and country. A hosted instance logs requests and rolls them into a per-day aggregate; a self-hosted deployment chooses whether and how long (`LOG_REQUESTS`, `LOG_RETENTION_DAYS`).

So the honest way to run CORX is self-hosting: one MIT-licensed Worker in an account you control. A hosted instance is a shared, best-effort demo — read the terms of use and the trust model before sending it traffic.

Terms of use · Trust model

Self-hosting

Every limit on this page — quotas, rate limits, retention, allowed hosts — is a setting on a Worker you control once you deploy your own copy. The README covers the ten-minute deployment (D1 + R2 + `wrangler deploy`); CONTRIBUTING.md covers local development and the checks a change has to pass.

Deployment guideContributing guide