corsproxy
CORS proxy server with SSRF protection, host allow/blocklisting, and optional API key authentication
About corsproxy
š CORS Proxy - Open Source Edition
A lightning-fast, simple CORS proxy server written in Go. Deploy anywhere with one click!
š Live Demo
Try it now: https://corsproxy-8uo5.onrender.com
Example:
# Fetch GitHub API through the proxy
curl "https://corsproxy-8uo5.onrender.com/?url=https://api.github.com/users/melihbirim"
# Use in JavaScript
fetch('https://corsproxy-8uo5.onrender.com/?url=https://api.example.com/data')
.then(r => r.json())
.then(data => console.log(data));
⨠Features
- ā” Fast: Written in Go for maximum performance
- š³ Docker Ready: Full Docker and Docker Compose support
- š One-Click Deploy: Deploy to Railway, Render, Fly.io, or Koyeb
- š Full CORS Support: Handles all CORS headers automatically
- š¦ Zero Dependencies: Uses only Go standard library
- š Secure: 10MB request size limit, 30s timeout
- š¾ Lightweight: ~10MB Docker image (Alpine-based)
šÆ Quick Start
Local Development
# Clone the repository
git clone https://github.com/melihbirim/corsproxy.git
cd corsproxy
# Run directly with Go
go run main.go
# Or use Make
make run
# Or build and run
make build
./bin/corsproxy
Server starts at http://localhost:8080
Run Tests
# Make sure server is running in another terminal
make test
# Or run directly
./test.sh
Using Docker
# Build and run with Docker
docker build -t corsproxy .
docker run -p 8080:8080 corsproxy
# Or use Docker Compose
docker-compose up
Development with Hot Reload
# Using the development Dockerfile
docker build -f Dockerfile.dev -t corsproxy-dev .
docker run -p 8080:8080 -v $(pwd):/app corsproxy-dev
š Usage
Basic Request
curl "http://localhost:8080/?url=https://api.example.com/data"
From JavaScript
fetch("http://localhost:8080/?url=https://api.example.com/data")
.then((response) => response.json())
.then((data) => console.log(data));
With Custom Headers
fetch("http://localhost:8080/?url=https://api.example.com/data", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Request-Id": "abc123",
},
body: JSON.stringify({ key: "value" }),
})
.then((response) => response.json())
.then((data) => console.log(data));
Not forwarded to the target, on purpose: Authorization, Cookie, and (if REQUIRE_API_KEY is on) X-API-Key. These are credential-bearing headers ā forwarding them unmodified would let anyone use your proxy to relay someone else's bearer token or session cookie to a third-party API, or leak your own configured API_KEYS value to the target. All other headers, including custom ones, pass through unchanged.
Health Check
curl http://localhost:8080/health
Response:
{ "status": "ok", "timestamp": "2026-01-08T12:00:00Z" }
š One-Click Deployments
Railway
- Click the "Deploy on Railway" button above
- Connect your GitHub repository
- Railway will auto-detect and deploy
- Your proxy will be live at
https://your-app.railway.app
Render
- Click the "Deploy to Render" button above
- Connect your repository
- Render will build and deploy automatically
- Access at
https://your-app.onrender.com
Fly.io
# Install flyctl
curl -L https://fly.io/install.sh | sh
# Login
flyctl auth login
# Deploy
flyctl launch
Koyeb
# Install Koyeb CLI
curl -fsSL https://cli.koyeb.com/install.sh | sh
# Login
koyeb login
# Deploy
koyeb app create corsproxy \
--git github.com/melihbirim/corsproxy \
--git-branch main \
--ports 8080:http \
--routes /:8080
āļø Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
PORT | 8080 | Server port |
MAX_REQUEST_SIZE | 10485760 | Max request size in bytes (10MB default) |
REQUEST_TIMEOUT | 30s | Request timeout (Go duration: 30s, 1m, etc) |
MAX_REDIRECTS | 10 | Maximum number of redirects to follow |
ALLOWED_ORIGINS | * | CORS allowed origins (* for all, or comma-separated list) |
ALLOWED_HOSTS | `` | Comma-separated list of allowed hosts (empty = all) |
BLOCKED_HOSTS | `` | Comma-separated list of blocked hosts |
ALLOW_PRIVATE_NETWORKS | false | Allow proxying to loopback/private/link-local addresses (only for trusted internal deployments) |
RATE_LIMIT_PER_MINUTE | 0 | Rate limit per IP (0 = disabled) |
TRUST_PROXY_HEADERS | false | Trust X-Forwarded-For for rate limiting (only enable behind a reverse proxy that actually sets it ā see below) |
REQUIRE_API_KEY | false | Require a valid key from API_KEYS on every proxied request |
API_KEYS | `` | Comma-separated list of accepted API keys (see below) |
DAILY_REQUEST_LIMIT | 0 | Global request ceiling per rolling 24h, across all clients (0 = disabled) |
VERBOSE_LOGGING | false | Enable detailed request logging |
Production Configuration Example
# .env file or deployment settings
PORT=8080
MAX_REQUEST_SIZE=5242880 # 5MB
REQUEST_TIMEOUT=15s
RATE_LIMIT_PER_MINUTE=100 # 100 requests per minute per IP
ALLOWED_ORIGINS=https://example.com,https://app.example.com,https://admin.example.com
ALLOWED_HOSTS=api.github.com,api.stripe.com,httpbin.org
BLOCKED_HOSTS=internal.example.com
DAILY_REQUEST_LIMIT=100000
VERBOSE_LOGGING=true
Security Features
CORS Origins:
# Allow all origins (development only)
ALLOWED_ORIGINS=*
# Allow specific origins (production recommended)
ALLOWED_ORIGINS=https://example.com,https://app.example.com
Host Filtering:
# Only allow specific APIs
ALLOWED_HOSTS=api.github.com,api.stripe.com
# Block specific hosts (matches the host and its subdomains)
BLOCKED_HOSTS=internal.example.com
# Private, loopback, link-local (incl. cloud metadata 169.254.169.254) and
# CGNAT addresses are always blocked, checked on the resolved IP of every
# connection, including redirects. Set ALLOW_PRIVATE_NETWORKS=true only if
# the proxy must reach internal services.
Rate Limiting:
# Limit to 100 requests per minute per IP address
RATE_LIMIT_PER_MINUTE=100
# Only set this if you're behind a reverse proxy/platform (Railway, Render,
# Fly, nginx, a CDN) that actually sets or overwrites X-Forwarded-For itself.
# Off by default: without a trusted proxy in front, a client can send any
# X-Forwarded-For value it wants and get a fresh rate-limit window on every
# request, making RATE_LIMIT_PER_MINUTE do nothing. Railway and Render's
# included configs (railway.json, render.yaml) already set this to true,
# since both platforms sit in front of the app.
TRUST_PROXY_HEADERS=true
Request Limits:
# Smaller file size limit for production
MAX_REQUEST_SIZE=5242880 # 5MB
# Faster timeout for better resource usage
REQUEST_TIMEOUT=15s
API Key Authentication (optional):
# Require a key on every proxied request (health checks are unaffected)
REQUIRE_API_KEY=true
API_KEYS=key-for-team-a,key-for-team-b
# Callers send it as a header or query param
curl -H "X-API-Key: key-for-team-a" "https://your-proxy.com/?url=https://api.github.com"
curl "https://your-proxy.com/?url=https://api.github.com&apikey=key-for-team-a"
Off by default so the zero-config quick start keeps working; turn it on once the proxy is reachable from outside your own machine.
Daily Request Ceiling (kill switch):
# Hard stop at 100k proxied requests per rolling 24h window, across every
# client combined ā a cost/abuse backstop independent of RATE_LIMIT_PER_MINUTE,
# which only limits a single IP.
DAILY_REQUEST_LIMIT=100000
This is process-local (an in-memory counter), so it resets on restart and doesn't coordinate across multiple replicas. Good enough as a single-instance cost cap; not a substitute for a shared rate limiter if you run more than one instance.
ā Production Checklist
The zero-config defaults are wide open, by design, so go run . and try-it-now demos work with no setup. Before exposing this on the public internet, go through this list:
- Set
ALLOWED_ORIGINSto your actual site(s). The default*lets any website's JavaScript call your proxy. - Set
ALLOWED_HOSTSto the specific upstream APIs you intend to proxy to. An empty list lets your proxy reach any public host, which makes it attractive as a free open relay for other people's traffic. - Set
RATE_LIMIT_PER_MINUTEto bound per-IP abuse, and setTRUST_PROXY_HEADERS=truealongside it if (and only if) you're deployed behind a reverse proxy/platform that actually setsX-Forwarded-Forā otherwise the rate limit is trivially bypassed by spoofing the header. - Set
DAILY_REQUEST_LIMITas a cost backstop if you're paying for egress/compute. - Consider
REQUIRE_API_KEYif the proxy is meant for your own apps/services rather than the general public ā it turns an open relay into an authenticated one. - Leave
ALLOW_PRIVATE_NETWORKSoff unless you specifically need the proxy to reach internal/loopback addresses; it's an SSRF hole otherwise. - Run behind HTTPS (a platform load balancer, or a reverse proxy like Caddy/nginx) ā this server speaks plain HTTP.
- Watch the startup logs: a
ā ļø Running with open defaultsline lists exactly which of the above are still off.
šļø Project Structure
corsproxy/
āāā main.go # Main application
āāā go.mod # Go module file
āāā Makefile # Build automation
āāā test.sh # Test script
āāā Dockerfile # Production Docker image
āāā Dockerfile.dev # Development Docker image
āāā docker-compose.yml # Docker Compose config
āāā railway.json # Railway deployment config
āāā render.yaml # Render deployment config
āāā fly.toml # Fly.io deployment config
āāā koyeb.json # Koyeb deployment config
āāā .env.example # Environment variables template
āāā README.md # This file
š ļø Makefile Commands
make help # Show all available commands
make build # Build the binary
make run # Run the server
make test # Run tests
make docker-build # Build Docker image
make docker-run # Run Docker container
make clean # Clean build artifacts
make fmt # Format code
make lint # Lint code
š§ API Endpoints
GET/POST/PUT/DELETE/PATCH /?url=<target-url>
Proxies the request to the target URL with CORS headers.
Query Parameters:
url(required): The target URL to proxy
Example:
curl "http://localhost:8080/?url=https://api.github.com/users/octocat"
GET /health
Health check endpoint for monitoring.
Response:
{ "status": "ok", "timestamp": "2026-01-08T12:00:00Z" }
š”ļø Security Features
- URL validation (must start with http:// or https://)
- Request size limiting (10MB max)
- Request timeout (30s)
- Redirect limit (max 10 redirects)
- No arbitrary code execution
š Performance
- Cold Start: < 100ms
- Request Latency: < 50ms overhead
- Memory Usage: ~10MB base
- Concurrent Requests: Thousands (Go's goroutines)
š License
MIT License - feel free to use this in your projects!
š¤ Contributing
We welcome contributions! This project is perfect for learning Go and building production-ready software.
Good First Issues
Looking to contribute? Check out our Good First Issues - production enhancements perfect for first-time contributors:
Priority 1 (Great for beginners):
- Add graceful shutdown handling
- Implement request ID middleware
- Add structured JSON logging
- Add security headers (CSP, HSTS, etc)
Priority 2 (Intermediate):
- Add Prometheus metrics endpoint
- Implement response caching
- Add API key authentication
- Add circuit breaker pattern
See CONTRIBUTING.md for detailed guidelines.
Quick Start for Contributors
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests:
make test - Run linter:
make lint - Commit your changes (
git commit -m 'Add: amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
š Comparison with Other Solutions
| Feature | This Proxy | CORS Anywhere | corsproxy-node |
|---|---|---|---|
| Language | Go | Node.js | Node.js |
| Docker Support | ā | ā ļø | ā |
| One-Click Deploy | ā | ā | ā ļø |
| Memory Usage | ~10MB | ~50MB | ~40MB |
| Cold Start | <100ms | ~1s | ~800ms |
| Dependencies | 0 | Many | Many |
š” Use Cases
- Bypass CORS restrictions in development
- Access APIs that don't support CORS
- Build web applications that need to fetch external resources
- Create API gateways with CORS support
- Testing and prototyping
ā ļø Production Considerations
While this proxy is production-ready, consider:
- Rate Limiting: Add rate limiting for public deployments
- Authentication: Add API keys if needed
- Monitoring: Use the
/healthendpoint for uptime monitoring - Logging: Logs are written to stdout (Docker-friendly)
- Caching: Consider adding caching for frequently accessed resources
š Troubleshooting
Port already in use
# Change port via environment variable
PORT=3000 go run main.go
Docker build fails
# Clean Docker cache
docker builder prune
docker build --no-cache -t corsproxy .
Connection timeout
The proxy has a 30-second timeout. For longer requests, modify the client.Timeout in main.go.
š Support
- Open an issue on GitHub
- Check existing issues for solutions
- Read the code - it's simple and well-commented!
Made with ā¤ļø by @melihbirim
Star ā this repository if you find it useful!
Frequently Asked Questions
What is corsproxy?
corsproxy is a Networking library for the Go programming language. CORS proxy server with SSRF protection, host allow/blocklisting, and optional API key authentication
How do I install corsproxy?
Install corsproxy with the Go module system using `go get melihbirim/corsproxy`. Check the repository for the current installation instructions.
What category does corsproxy belong to?
corsproxy is listed under Networking, specifically Networking.