# cloudflare-cors-anywhere: a CORS proxy you deploy yourself as a Cloudflare Worker

> cloudflare-cors-anywhere is a small Cloudflare Worker that fetches any URL on behalf of a browser and returns the response with permissive CORS headers, so a page can call an API that does not send them. It has no binary distribution and no dependencies, and the published demo is explicitly reserved for testing.

**Zibri/cloudflare-cors-anywhere** — CORS "anywhere" proxy in a Cloudflare worker. DEMO at: https://test.cors.workers.dev/

- Repository: https://github.com/Zibri/cloudflare-cors-anywhere
- Stars: 836 · Forks: 430
- Language: JavaScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/zibri-cloudflare-cors-anywhere

## What the Worker actually does

cloudflare-cors-anywhere is a CORS proxy running in a Cloudflare Worker, and the whole implementation is small enough to describe in one sentence: it fetches the URL you give it and returns the response with headers that let a browser read it.

The usage example shows the interface. The target URL is passed as a query parameter, with the original URL after a question mark:

```javascript
fetch('https://test.cors.workers.dev/?https://httpbin.org/post', {
  method: 'post',
  headers: {
    'x-foo': 'bar',
    'x-bar': 'foo',
    'x-cors-headers': JSON.stringify({
      // allows to send forbidden headers
      // https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name
      'cookies': 'x=123'
    }) 
  }
}).then(res => {
  // allows to read all headers (even forbidden headers like set-cookies)
  const headers = JSON.parse(res.headers.get('cors-received-headers'))
  console.log(headers)
  return res.json()
}).then(console.log)
```

Two extension points stand out. The x-cors-headers request header carries a JSON object of headers the browser would normally forbid, which the proxy attaches to the outgoing request. The comment links to the MDN glossary entry for forbidden header names, which is where the list of what a browser refuses to let page script set lives. Cookies are the example given.

The other extension point is on the response side. All the headers the proxy received are returned to the caller in a response header named cors-received-headers, and the example parses that header as JSON to inspect it. The comment notes that this lets you read all headers, including forbidden ones like set-cookies that the browser would otherwise hide.

That response header is the feature that makes the tool useful for more than just getting data through. When a request fails for reasons you cannot see from the browser's console, being able to inspect what the origin actually returned is often the difference between a diagnosis and a guess.

## Deploying it with Wrangler

The project is written for Cloudflare Workers and is deployed with the Wrangler CLI, which the README points at as the deployment route:

```bash
npx wrangler deploy
```

That is the entire deployment procedure. The README states that creating your own is very easy and that you need a Cloudflare account and to upload the worker code, and the deploy button at the top of the README offers the same path through the Cloudflare dashboard, linking to the workers and pages creation flow pre-filled with this repository.

The repository contents match that simplicity. There is an index.js holding the worker, a wrangler.toml with the worker configuration, a package.json, a README, a licence file named LICENSE_MIT, and a HELP_NEEDED.md file.

The package.json is worth reading for what it reveals about the project's shape. The name is a template placeholder, {{ project-name }}, and the author field is {{ authors }}, which is the Wrangler template convention for values you fill in when you initialise a project. The version is 1.0.0, the description is a CORS proxy running in a worker, and the main entry is index.js.

There are no runtime dependencies at all. The only dev dependencies are husky at 2.1.0 and prettier at 1.17.0, and the husky configuration runs the format script on both pre-commit and pre-push, where the format script is prettier over js, css, json and md files. The test script is the placeholder that echoes an error and exits with 1, so the project ships with a formatter and hooks but no test suite.

## The demo instance is a testing convenience, not an infrastructure tier

The README includes a live demo at test.cors.workers.dev, and it is careful about what that instance is for. The stated rule is that abuse of the demo, other than testing, will result in a ban, and the demo accepts only fetch and xmlhttprequest.

That restriction on the HTTP methods is a good signal about how the Worker is written. A proxy that only handles GET and POST style requests, and only from browser APIs, is not going to serve as a general-purpose open relay, and refusing other methods is the first and cheapest line of defence.

The demo exists because the alternative for a developer who needs to test a CORS workaround is to sign up for a Cloudflare account, and that friction is real for a five-minute experiment. The README points out that creating your own is easy for exactly that reason, and the deployment instructions are right there.

The README also records a personal note of thanks to Damien Collis for a donation, and links a donation channel plus an older blog post about running your own CORS anywhere proxy on Cloudflare Workers, which is the kind of background reading that helps explain why the project is shaped this way.

## What a CORS proxy is for, and what it costs you

The reason this exists is the browser's same-origin policy. A page served from one origin cannot read the response of a request to another origin unless that other origin opts in with the right response headers. That rule is deliberate and it is the reason a lot of APIs are usable from a server but not from a browser page.

A proxy sidesteps it by making the request from a different origin and handing back a response with permissive headers. The browser sees a same-origin request to the proxy, the proxy sees a cross-origin request it is allowed to make, and the page gets its data.

The cost is that you have placed a server between the browser and the API, and that server is now responsible for what it forwards. Any request a browser can make to your proxy, anyone can make to your proxy. If your Worker forwards any URL to any destination and answers with permissive CORS headers, then your Worker is an open relay that anyone can use to make requests from your address, and the responses come back with the origin's headers visible.

For a local debugging session or a demo that is a reasonable trade. For anything user-facing, the same code needs an origin allowlist and a destination allowlist, and the upstream API should be called with credentials rather than through a general proxy.

## Where cloudflare-cors-anywhere is the wrong choice

The clearest objection is that most CORS problems do not need a proxy at all. The right fix in almost every case is on the server: the API adds an Access-Control-Allow-Origin header, and the browser is satisfied. The proxy is a workaround for a server you do not control or cannot change, and a workaround like that tends to hide a real integration issue rather than solve it.

The second objection is that if your own backend can reach the API, your backend should call the API and your frontend should call your backend. That is one more network hop but it keeps the API credentials on the server where they belong, and it gives you a place to cache responses. A proxy in the browser path is a solution for client-side applications with no server at all, which is a narrower situation than it first appears.

The third is operational. A Worker is a deployed resource with a URL, and if you deploy one that forwards arbitrary requests, you have created a public service that will be found. The project does not include an allowlist, a rate limit, an authentication scheme or a test suite, so all of that is yours to add, and the README does not describe it. The MIT licence and the template-style package.json also signal that this is a starting point rather than a maintained service with a security model.

The alternative to weigh is a purpose-built API gateway or a scraping service that signs requests and caches responses, which costs money and gives you reliability and limits. For a two-hour debugging session, deploying your own Worker is the fastest route and the cost is a few lines of configuration.

## Conclusion

cloudflare-cors-anywhere fits a developer who needs to read a cross-origin API from browser code for a tool, a demo or a debugging session, and who is willing to deploy their own Worker so their traffic does not share a public instance with everyone else. It does not fit an application in production, since a proxy that forwards any request from any origin is an open relay unless you add controls of your own, and it does not fit a backend that can simply make the server-side call itself. Before deploying, read the source of index.js to see exactly what the Worker forwards and what it returns, decide how you will restrict which origins may use it, and treat the demo instance as a testing convenience only, since the README states that abuse other than testing results in a ban.

## FAQ

### What does the cloudflare-cors-anywhere Worker do?

It proxies a request to any URL given as a query parameter and returns the response with headers that let browser code read it, so a page can call an API that does not send CORS headers. It runs entirely as a Cloudflare Worker with no runtime dependencies.

### How do I deploy my own cloudflare-cors-anywhere instance?

You need a Cloudflare account and deploy the worker with Wrangler using `npx wrangler deploy`. The README also links a deploy button that creates the worker from this repository through the Cloudflare dashboard.

### What is the cors-received-headers response header for?

It carries all the headers the proxy received from the origin, as JSON, which lets your code read headers the browser would otherwise hide, including forbidden ones such as set-cookie. It is the useful part when you need to diagnose what an API actually returned.

### How do I send headers the browser normally forbids?

Pass them in the x-cors-headers request header as a JSON object, for example a cookies entry, and the proxy attaches them to the outgoing request. The README links the MDN glossary entry on forbidden header names for the full list.

### Can I use the public demo worker for production traffic?

The README says abuse of the demo other than testing will result in a ban, and the demo accepts only fetch and xmlhttprequest. Creating your own instance is described as very easy, which is the intended route for anything beyond testing.

## Sources

- [Issues](https://github.com/Zibri/cloudflare-cors-anywhere/issues)
- [License: MIT](https://github.com/Zibri/cloudflare-cors-anywhere/blob/master/LICENSE)
- [README](https://github.com/Zibri/cloudflare-cors-anywhere/blob/master/README.md)
- [Zibri/cloudflare-cors-anywhere on GitHub](https://github.com/Zibri/cloudflare-cors-anywhere)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zibri-cloudflare-cors-anywhere
