DEV Community

Melih Birim
Melih Birim

Posted on Fully Autonomous

Self-host a CORS proxy in 60 seconds (a Go alternative to cors-anywhere)

You're building a frontend, you call a third-party API, and the browser stops you:

Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present.
Enter fullscreen mode Exit fullscreen mode

The API works fine from curl. It just doesn't send CORS headers, so the browser won't let your JavaScript read the response. The usual fixes are to write a backend route just to forward the call, or to use a public CORS proxy and hope it's still up (and not logging your tokens) tomorrow.

A third option is to run your own proxy. I built corsproxy for this: a single Go binary, standard library only, and a ~10MB Docker image.

Run it

git clone https://github.com/melihbirim/corsproxy.git
cd corsproxy
docker compose up
Enter fullscreen mode Exit fullscreen mode

Or without Docker:

go run main.go
Enter fullscreen mode Exit fullscreen mode

It listens on http://localhost:8080.

Use it

Prefix the target URL with ?url=:

fetch("http://localhost:8080/?url=https://api.github.com/users/octocat")
  .then((r) => r.json())
  .then(console.log);
Enter fullscreen mode Exit fullscreen mode

GET, POST, PUT, PATCH and DELETE all pass through, with headers and body. There's a /health endpoint for uptime checks.

Don't run an open relay

The defaults are wide open, so the quick start works with no setup. Before you put it on the public internet, lock it down with environment variables:

ALLOWED_ORIGINS=https://myapp.com          # only your site can call it
ALLOWED_HOSTS=api.github.com,api.stripe.com # only these upstream APIs
RATE_LIMIT_PER_MINUTE=100                   # per client IP
DAILY_REQUEST_LIMIT=100000                  # global cost backstop
REQUIRE_API_KEY=true
API_KEYS=key-for-my-app
Enter fullscreen mode Exit fullscreen mode

Private, loopback and cloud-metadata addresses (like 169.254.169.254) are blocked by default, checked on the resolved IP of every connection including redirects, so the proxy can't be used to reach your internal network (SSRF).

On startup it logs a warning listing any of these protections that are still off.

Deploy

The repo has configs for Railway, Render, Fly.io and Koyeb, so you can deploy with one click or one CLI command. Put it behind HTTPS (the platform's load balancer, or Caddy/nginx), since the server itself speaks plain HTTP.

Compared to cors-anywhere

cors-anywhere is the classic option, but it's Node.js with dependencies. corsproxy is one Go binary with no dependencies, around 10MB of memory, and cold starts under 100ms, which matters on platforms that scale to zero.

Links

If it saves you a backend route, a star helps others find it.

Top comments (0)