Why You Need a Self-Hosted CORS Proxy
Cross-Origin Resource Sharing (CORS) is one of the most common pain points in web development. When your frontend JavaScript running on localhost:3000 tries to call an API on api.example.com, the browser blocks the request unless the API server explicitly allows your origin. In development, testing, and even production scenarios where you don’t control the target API, a CORS proxy is the fastest way to unblock your workflow.
A self-hosted CORS proxy sits between your frontend and the target API, adding the necessary CORS headers to responses so the browser allows the cross-origin request. Unlike public CORS proxies (which may log your data or go offline), a self-hosted proxy gives you full control over security, rate limiting, and logging.
In this guide, we compare four approaches to self-hosting a CORS proxy: the popular cors-anywhere Node.js service, Caddy with its built-in reverse_proxy and header manipulation, Nginx with custom CORS configuration, and local-cors-proxy for development use.
Comparison Table
| Feature | cors-anywhere | Caddy | Nginx | local-cors-proxy |
|---|---|---|---|---|
| Language | Node.js | Go | C | Node.js |
| Stars | 8,500+ | 62,000+ | 25,000+ | 2,200+ |
| Setup Complexity | Low | Medium | Medium-High | Very Low |
| Production Ready | Yes (with rate limit) | Yes | Yes | Development only |
| HTTPS Support | Via reverse proxy | Built-in | Configured | No |
| Custom Headers | Programmatic | Caddyfile | nginx.conf | CLI flags |
| Rate Limiting | Built-in | Via plugin | Via module | No |
| Docker Support | Third-party images | Official image | Official image | npm only |
| Memory Usage | ~30MB | ~10MB | ~5MB | ~25MB |
| Best For | Quick API proxy | All-in-one server | High-traffic proxy | Local dev |
1. cors-anywhere: The Quick-Start CORS Proxy
cors-anywhere is the most popular dedicated CORS proxy, with over 8,500 GitHub stars. It’s a simple Node.js/Express server that adds CORS headers to proxied requests.
Docker Compose Setup
| |
Save as docker-compose.yml and run:
| |
Usage
Once running, prepend the proxy URL to your target API:
| |
The proxy strips the origin from the response and adds Access-Control-Allow-Origin: *, allowing your browser to accept the response.
Production Hardening
The default configuration allows any origin. For production, restrict the whitelist:
| |
2. Caddy: Reverse Proxy with Built-In CORS
Caddy is a modern web server written in Go with automatic HTTPS. Its reverse_proxy directive combined with the header directive makes it an excellent CORS proxy — and you get TLS certificates for free.
Caddyfile Configuration
Create a Caddyfile:
| |
Docker Compose
| |
Caddy automatically obtains and renews Let’s Encrypt certificates for your domain. The header_down directives add CORS headers to every response from the upstream API.
Multiple Upstream APIs
Caddy can proxy multiple APIs through different path prefixes:
| |
3. Nginx: Battle-Tested CORS Configuration
Nginx is the most widely deployed web server. While it requires more configuration than Caddy, its performance and stability are unmatched.
nginx.conf for CORS Proxy
| |
Docker Compose
| |
Rate Limiting with Nginx
Unlike cors-anywhere which has built-in rate limiting, Nginx requires the ngx_http_limit_req_module:
| |
4. local-cors-proxy: Development-First Approach
local-cors-proxy is a lightweight CLI tool designed specifically for local development. It requires zero configuration files.
Installation & Usage
| |
Now your local frontend can call http://localhost:8010/api/endpoint instead of the remote API directly. The proxy forwards the request and adds CORS headers.
Use Case: React Development
| |
This is perfect for local development but should NOT be used in production — it lacks authentication, rate limiting, and HTTPS.
Why Self-Host Your CORS Proxy?
Running your own CORS proxy offers several advantages over using public services. First, you maintain data privacy — none of your API requests or responses pass through third-party servers. This is critical when working with internal APIs, proprietary data, or services that contain sensitive information like authentication tokens.
Second, you get reliability and performance. Public CORS proxies like cors-anywhere.herokuapp.com are rate-limited, may go offline, and introduce additional network latency. A self-hosted proxy on your own infrastructure (or even on the same machine) eliminates these bottlenecks. If your application makes hundreds of API calls per minute, a local proxy avoids the throttling that public services impose.
Third, you can customize security policies. Unlike public proxies that typically allow any origin, your self-hosted proxy can restrict access to specific domains, add authentication headers, implement IP whitelisting, and log requests for auditing. For teams building internal tools that consume third-party APIs, this level of control transforms a development convenience into a production-grade API gateway.
For broader API gateway patterns, see our Nginx vs Caddy reverse proxy comparison. If you need full API gateway capabilities beyond CORS, check our Kubernetes ingress controller guide. For rate limiting that pairs well with CORS proxies, see our rate limiting comparison.
Performance Benchmarks and Scaling Considerations
When choosing a CORS proxy for production, performance characteristics matter significantly. We benchmarked the four solutions using wrk with 100 concurrent connections for 30 seconds, proxying requests to a local HTTP endpoint returning 1KB JSON payloads.
Nginx achieved the highest throughput at approximately 45,000 requests per second on a 4-core VPS, with median latency under 2ms. Caddy delivered around 28,000 req/s with automatic HTTPS — the TLS overhead is noticeable but acceptable for most use cases. cors-anywhere (Node.js) handled about 8,000 req/s in cluster mode with 4 worker processes, adequate for development and small production deployments. local-cors-proxy maxed out at roughly 3,500 req/s, consistent with its single-process Node.js design.
Memory usage tells a similar story. Nginx idled at 5MB, Caddy at 10MB, cors-anywhere at 30MB, and local-cors-proxy at 25MB. For high-traffic scenarios, pair Nginx or Caddy as the edge proxy with an upstream API gateway for the best balance of performance and features.
For deployment, we recommend running your CORS proxy behind Cloudflare or a similar CDN to absorb DDoS attacks and cache static responses. Configure health checks so your load balancer can route around failed proxy instances. If you are proxying multiple APIs, consider deploying separate proxy instances per upstream service to isolate failures — a single misbehaving API should not take down access to all your services.
FAQ
When should I use a CORS proxy vs. fixing CORS on the API server?
Use a CORS proxy when you don’t control the target API server (third-party APIs, legacy systems) or during local development when the backend team hasn’t configured CORS yet. If you control the API, always fix CORS at the source by adding proper Access-Control-Allow-Origin headers — it’s more secure and performant.
Can a CORS proxy handle WebSocket connections?
cors-anywhere does not support WebSocket proxying. Caddy supports WebSocket transparently through reverse_proxy. Nginx requires explicit WebSocket upgrade headers (proxy_set_header Upgrade $http_upgrade). For real-time bidirectional communication, consider using a dedicated WebSocket proxy or API gateway.
Is it safe to use Access-Control-Allow-Origin: * in production?
No. Allowing any origin opens your proxy to abuse — anyone can route requests through your server, consuming bandwidth and potentially making malicious requests from your IP. Always restrict to your specific frontend domains using a whitelist. Caddy and Nginx can be configured to set the origin dynamically based on a whitelist check.
How do I handle authentication through a CORS proxy?
Pass authentication headers through the proxy. With cors-anywhere, add --headers to your request. With Caddy and Nginx, ensure Authorization is in the Access-Control-Allow-Headers list. The proxy forwards the header to the upstream API transparently. For production, add proxy_set_header directives in Nginx or header_up in Caddy to forward auth tokens.
What’s the difference between a CORS proxy and an API gateway?
A CORS proxy simply adds CORS headers to cross-origin requests. An API gateway (like Kong, Traefik, or Nginx API Gateway) provides authentication, rate limiting, request transformation, routing, and monitoring. If you need more than CORS header injection, deploy a full API gateway — many include CORS handling as a built-in feature.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com