GuidesAPI KEYS
CORS proxy for Authorization headers and API keys
cors.dev Pro forwards Authorization, X-Api-Key and ?api_key= from the browser: $5 a month for 500,000 requests, 7-day trial, no card. The trial includes 1,000 requests. Your Connection key only works from the exact origins you register, like https://app.example.com or http://localhost:5173, and is never sent upstream. The free anonymous proxy refuses all three, so credentials never cross a shared, open endpoint.
Forward your first Authorization header
- Start the trial
Sign in with Google and start the 7-day Pro trial from the dashboard: 1,000 requests, no card.
- Create a Connection
Add the exact origins of your app, up to 3, and the API hosts it calls, up to 5. An origin is scheme, host and port:
https://app.example.comandhttp://localhost:5173are two different origins. - Copy the Connection key
The dashboard shows the key once. It is a publishable identifier, so it can live in your frontend config next to the API base URL.
- Send both headers
Put the key in
X-Cors-Keyand the credential inAuthorizationor whichever header the API expects. The proxy checks key, origin and host, removesX-Cors-Keyand forwardsAuthorizationand your API headers.
const PROXY = 'https://proxy.cors.dev/';
const CORS_KEY = 'YOUR_CONNECTION_KEY'; // publishable, works only from your registered origins
const me = await fetch(PROXY + 'https://api.example.com/v1/me', {
headers: {
'X-Cors-Key': CORS_KEY,
Authorization: `Bearer ${accessToken}`,
},
credentials: 'omit',
});
const reports = await fetch(PROXY + 'https://api.example.com/v1/reports', {
headers: { 'X-Cors-Key': CORS_KEY, 'X-Api-Key': userApiKey },
credentials: 'omit',
});Why another site cannot use your key
The browser sets the Origin header on every cross-origin request, and page scripts cannot change it. The proxy compares it with the origins on your Connection character for character and answers 403 with X-Cors-Error: origin_not_allowed when it does not match or is missing, which also shuts out null origins from sandboxed iframes and local files. A copy of your key on someone else's site does nothing there.
Outside a browser anyone can type an Origin header, so treat the Connection key like a Stripe publishable key, not like a password. It reaches only the hosts you enabled, redirect hops included, and anything else gets 403 target_not_allowed. It carries no upstream credential of its own, and you can rotate or revoke it from the dashboard. The worst a leaked key can do is spend your request allowance on calls to your own enabled APIs, with credentials the caller has to bring.
What the proxy sees and keeps
Like any proxy, CDN or API gateway in the path, cors.dev holds your Authorization header in memory while it relays the request over HTTPS. It does not keep it: application logs record the destination host, status and timing, not URLs, query strings, keys, tokens or bodies. Requests that carry Authorization, an API key header or a credential-named query parameter are never cached, even with X-Cors-Cache set, so one user's response is never served to another. Cookies and your Connection key are never forwarded.
Which credentials belong in a browser
Forwarding a header does not make it secret: anything your frontend sends, a visitor can read in DevTools, with or without a proxy. What matters is whether the credential was meant to be there.
- Good fit: an access token your user got from an OAuth login with PKCE, an API key the user pastes into your tool, or a key the provider issues for browser use and restricts by domain.
- Good fit: an internal dashboard where every user may hold the token they send.
- Wrong place: your own paid server key for an AI, payment, email or database API. Anyone who opens your app can copy it and run up your bill. Keep it in a serverless function with a secret environment variable; API proxy without a server compares the options.
Keys in the URL: ?api_key=, ?token= and the rest
Many APIs take the key as a query parameter. Anonymous requests refuse credential names such as key, api_key, apikey, token, access_token, auth, signature and secret with a 400 before anything leaves the proxy. With a trial or Pro Connection key the same URL is forwarded unchanged and the response is never cached.
const target = new URL('https://api.example.com/v1/search');
target.searchParams.set('q', 'cors');
target.searchParams.set('api_key', userApiKey);
const response = await fetch('https://proxy.cors.dev/' + target, {
headers: { 'X-Cors-Key': 'YOUR_CONNECTION_KEY' },
credentials: 'omit',
});Prefer a header when the API accepts one: a key in a URL can also end up in the API provider's access logs and in browser history.
Anonymous vs Pro, credential by credential
| You send | Anonymous, free | Pro, $5 per month |
|---|---|---|
| Authorization: Bearer token | Refused with 400 | Forwarded unchanged |
| X-Api-Key or another API key header | Refused with 400 | Forwarded unchanged |
| ?api_key= or ?token= in the URL | Refused with 400 | Forwarded, never cached |
| Cookies | Never forwarded | Never forwarded |
| X-Cors-Key | Not used | Checked, then removed before the API |
| Who can call | Any website | Your origins, up to 3 per Connection |
| APIs it reaches | Any public HTTPS host | Hosts you enable, up to 5 per Connection |
| Methods | GET and HEAD | GET, HEAD, POST, PUT, PATCH and DELETE |
| Response size | 1 MiB, text types | 6 MiB, any content type |
| Rate | 600 requests per minute per IP, shared pool | 600 requests per minute, 10 concurrent |
| Price | $0, no signup | $5 per month for 500,000 requests; 7-day trial with 1,000 |
Good questions.
Is it safe to send an API key through a CORS proxy?
As safe as putting that key in your frontend, because that is where it already is. For a user's own token or a browser key restricted by domain, yes: cors.dev forwards it over HTTPS, does not log it and never caches responses to requests that carry it. For your own secret server key, no proxy makes it safe; move that call into a serverless function.
Can another website use my Connection key?
Not from a browser. Browsers set the Origin header themselves, and the proxy answers 403 origin_not_allowed for any origin you did not register. Scripts outside a browser can fake an Origin, which is why the key only reaches the hosts you enabled and holds no upstream credential. Rotate it in the dashboard if it is abused.
Why does the free proxy answer 400 header_not_allowed?
Anonymous requests refuse Authorization and API key headers such as X-Api-Key, and credential-named query parameters such as api_key or token get a 400 too. Forwarding them needs a Connection key on the trial or on Pro.
Does it work from localhost?
Yes. Register http://localhost:5173, or whichever port your dev server uses, as one of the 3 origins on your Connection. The port is part of the origin, so http://localhost:3000 needs its own entry.
Does a free account unlock Authorization headers?
No. Signing in without the trial or Pro keeps the anonymous rules: GET and HEAD, no Authorization, no keys in the URL, 120 requests per minute. The 7-day trial and Pro forward credentials.
Can the proxy add my secret key on the server side?
No. cors.dev forwards only what your page sends and stores no upstream credentials. If a key must stay hidden from your users, a Cloudflare Worker, Vercel Function or Netlify Function that holds it as a secret is the right tool.