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

  1. Start the trial

    Sign in with Google and start the 7-day Pro trial from the dashboard: 1,000 requests, no card.

  2. 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.com and http://localhost:5173 are two different origins.

  3. 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.

  4. Send both headers

    Put the key in X-Cors-Key and the credential in Authorization or whichever header the API expects. The proxy checks key, origin and host, removes X-Cors-Key and forwards Authorization and your API headers.

Forward a Bearer token or an X-Api-Key header
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.

An API key in the query string, Pro only
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

What cors.dev forwards without a key and with a Pro Connection
You sendAnonymous, freePro, $5 per month
Authorization: Bearer tokenRefused with 400Forwarded unchanged
X-Api-Key or another API key headerRefused with 400Forwarded unchanged
?api_key= or ?token= in the URLRefused with 400Forwarded, never cached
CookiesNever forwardedNever forwarded
X-Cors-KeyNot usedChecked, then removed before the API
Who can callAny websiteYour origins, up to 3 per Connection
APIs it reachesAny public HTTPS hostHosts you enable, up to 5 per Connection
MethodsGET and HEADGET, HEAD, POST, PUT, PATCH and DELETE
Response size1 MiB, text types6 MiB, any content type
Rate600 requests per minute per IP, shared pool600 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.