CORS proxy cookie forwarding: what we fixed
A CORS proxy takes whatever headers your request carries and, unless it specifically decides not to, sends them straight on to the target URL you gave it. The hosted corsproxy.dev service already excluded Authorization and X-API-Key from that passthrough. Cookie was the one we'd missed. It's fixed in the hosted service now, and the underlying problem is worth understanding if you run a proxy of your own, including our open-source one.
Why this doesn't come up for most browser calls
If you call corsproxy.dev from fetch() in a browser, you can't set a Cookie header on the request yourself: it's one of the forbidden header names in the Fetch spec, alongside Host and Referer. The browser manages it. And fetch's default credentials mode, 'same-origin', doesn't attach cookies to a cross-origin request at all; a page has to opt in with credentials: 'include'. Access-Control-Allow-Credentials doesn't control whether cookies are sent, only whether the page may read a credentialed response, and corsproxy.dev never sends it (see CORS with cookies and credentials for the full mechanics).
So this gap didn't affect a typical frontend calling the proxy with fetch. It affected anything that isn't a browser: server-side code, a cron job, Postman, a curl command, a mobile app's HTTP client. None of those are bound by the Fetch spec's forbidden-header list — they can set Cookie to whatever they want, and until recently, the hosted proxy forwarded it unchanged.
The actual risk
The proxy's whole job is to fetch a URL you give it and return the response. If your calling code sets a Cookie header (maybe because an HTTP client library attaches a stored cookie jar automatically, maybe because you copy-pasted a request from one target to another), that cookie now travels to whatever url= you pass, not just the domain the cookie was meant for. Point the same client at a different host, by typo or by design, and the cookie goes there too. That's the same shape of problem as forwarding a bearer token: a credential meant for one destination leaking to another because the proxy didn't ask whether it should relay it.
It's a narrower issue than classic SSRF, since it requires the caller to be setting a Cookie header at all, but it's the same category of mistake we already cover in how a CORS proxy can be abused: a proxy that passes a header through by default, without an explicit decision to do so, will eventually send it somewhere nobody intended.
What changed
The hosted proxy already excluded a specific list of inbound headers before forwarding a request upstream: Host, Origin, Referer, X-API-Key, and Authorization. Cookie is now on that list too. The check is a plain denylist applied to every inbound request, before any upstream rule or per-request override is added, so there is no code path that forwards the caller's cookie by accident.
If you send a Cookie header to api.corsproxy.dev today, it's dropped before the request leaves our network. It never reaches the target, and it isn't logged with the request either.
If you need to send a cookie upstream
Sometimes the target expects a cookie: a legacy API that authenticates with a session cookie instead of a header, for instance. For that, use an explicit reqHeaders override rather than relying on passthrough:
curl "https://api.corsproxy.dev/proxy?url=https%3A%2F%2Fapi.example.com%2Fdata&key=sk_live_...&reqHeaders=cookie:session=abc123"
The distinction matters: passthrough happens whether you meant it to or not, just because the header was present on the inbound request. An override is a value you typed into this specific request, for this specific target. On the way back, the hosted proxy strips Set-Cookie from upstream responses and won't let you set it via resHeaders, for the same reason in reverse: we don't want a proxied response silently setting cookies in the browser's eyes for our own domain.
The general lesson, if you run your own proxy
Our own open-source Go proxy is a separate, simpler program from the hosted service, and it has not had this fix. main.go forwards every request header except Host, including Cookie and Authorization, and passes every response header back, including Set-Cookie. If you self-host it, strip those headers yourself (in the code or in a reverse proxy in front of it), and restrict it with ALLOWED_HOSTS so a forwarded credential can only reach targets you chose.
Whatever proxy you run, the header-passthrough list deserves a specific, written-down answer, not "whatever the HTTP library forwards by default." At minimum:
Authorization: don't relay a caller's bearer token to an arbitrary target; see keeping API keys out of the browser for the alternative.Cookie(andSet-Cookieon the response): same reasoning, and easy to miss because browsers partly hide the problem from you during normal testing.- Any custom auth header your own infra uses (session tokens, internal service headers): if it's meaningful to one of your own services, it shouldn't be meaningful to an arbitrary
url=parameter.
None of this replaces target-side protections like blocking private-network addresses; see how a CORS proxy can be abused for that half of the threat model. Header stripping and target filtering are answering different questions: one is "should this credential go anywhere the caller points it," the other is "should this proxy be allowed to reach that address at all."
Reporting a security issue
Found something like this in corsproxy.dev? Email contact@corsproxy.dev. Our security contact details are also in security.txt.