A lightweight, high-performance CORS proxy built for Fermyon Spin that forwards HTTP requests while adding permissive CORS headers and prevents caching of stale data.
- π Fast & Lightweight: Built with Rust and WebAssembly for optimal performance
- π Full Request Forwarding: Supports all HTTP methods (GET, POST, PUT, DELETE, etc.)
- π Permissive CORS: Adds appropriate CORS headers for browser compatibility
- π« No Caching: Ensures all requests are passed through without caching
- π URL Validation: Basic security checks for target URLs
- π¦ Easy Deployment: Deploy to Fermyon Cloud or self-hosted Spin
- Rust (latest stable)
- Fermyon Spin v3.0+
wasm32-wasip2target:rustup target add wasm32-wasip2
git clone https://github.com/fschutt/corsproxy
cd corsproxy
spin build# Run locally
spin up
# Your proxy will be available at:
# http://localhost:3000Now, adjust the values in spin.toml for allowed outbound hosts and test with:
curl -H "x-target-url: https://www.google.com" http://localhost:3000
Now, you have your own, private proxy for deployment!
# Login to Fermyon Cloud
spin login
# Deploy
spin deployNow, note the target url and test again with:
curl -H "x-target-url: https://www.google.com" https://your-prod-url.fermyon.dev
The proxy accepts target URLs via two methods:
curl -H "x-target-url: https://api.example.com/data" \
https://your-proxy.spin.app/# Simple URL
curl "https://your-proxy.spin.app/?url=https://api.example.com/data"
# URL-encoded (for complex URLs with query parameters)
curl "https://your-proxy.spin.app/?url=https%3A//api.example.com/search%3Fq%3Dhello%20world"// Using fetch with header method
const response = await fetch('https://your-proxy.spin.app/', {
method: 'GET',
headers: {
'x-target-url': 'https://api.example.com/data'
}
});
// Using query parameter method
const response = await fetch(
'https://your-proxy.spin.app/?url=' +
encodeURIComponent('https://api.example.com/data')
);The proxy can be configured through the spin.toml file:
[component.cors-proxy]
# Allow outbound requests to any host
allowed_outbound_hosts = ["*"]
# Or restrict to specific domains
# allowed_outbound_hosts = ["https://api.example.com", "https://another-api.com"]The proxy automatically adds these CORS headers:
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEADAccess-Control-Allow-Headers: *Access-Control-Expose-Headers: *Access-Control-Max-Age: 86400(for preflight requests)
And these cache prevention headers:
Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidatePragma: no-cacheExpires: 0
- Private Deployment: This proxy is designed for private use. The URL is your security perimeter.
- No Rate Limiting: There's no built-in rate limiting since this is intended for private use.
- URL Validation: Only
http://andhttps://URLs are accepted. - Hop-by-hop Headers: Connection-level headers are automatically filtered out.
- Deploy to a private environment or use authentication
- Restrict
allowed_outbound_hostsin production - Monitor usage and implement rate limiting if needed
- Use HTTPS for your proxy deployment
# Start the proxy locally
spin up
# Test with curl
curl -v -H "x-target-url: https://httpbin.org/get" \
http://localhost:3000/
# Test preflight request
curl -v -X OPTIONS \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type" \
http://localhost:3000/Enable verbose logging:
RUST_LOG=debug spin upspin cloud deploy# Using Docker
docker run -p 3000:80 -v $(pwd):/app fermyon/spin:latest
# Or install Spin directly
spin up --listen 0.0.0.0:3000Update spin.toml for custom domains:
[application.trigger.http]
base = "/"
# Add custom domain configuration as needed| Method | Path | Description |
|---|---|---|
* |
/ |
Proxy endpoint - forwards to target URL |
OPTIONS |
/ |
Handles CORS preflight requests |
| Header | Description | Required |
|---|---|---|
x-target-url |
Target URL to proxy to | Yes (if not using query param) |
| Parameter | Description | Required |
|---|---|---|
url |
URL-encoded target URL | Yes (if not using header) |
| Code | Description |
|---|---|
200 |
Successful proxy response |
500 |
Proxy error (invalid URL, network error, etc.) |
"Missing target URL" error:
- Ensure you're providing either
x-target-urlheader orurlquery parameter
"Target URL must start with http://" error:
- Only HTTP and HTTPS URLs are supported
- Check URL encoding if using query parameters
Network errors:
- Verify
allowed_outbound_hostsinspin.toml - Check if target server is accessible
CORS issues:
- The proxy adds permissive CORS headers automatically
- Check browser dev tools for specific CORS errors
- Use header method for better performance
- Keep URLs reasonably short
- The proxy doesn't cache, so consider caching on the client side if needed
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
MIT License - see LICENSE file for details