Self-Hosting
For maximum privacy or enterprise requirements, you can run your own CORS Proxy instance. This guide covers deployment options from quick Docker setup to production Cloudflare Workers deployment.
Deployment Options
Section titled “Deployment Options”| Option | Best For | Effort |
|---|---|---|
| Docker | Quick local/server deployment | Low |
| Docker Compose | Production server deployment | Low |
| Cloudflare Workers | Global edge deployment, serverless | Medium |
| Node.js | Custom integrations | Medium |
Docker Deployment
Section titled “Docker Deployment”Quick Start
Section titled “Quick Start”Run the proxy with Docker:
docker run -p 3000:3000 ghcr.io/pondpilot/cors-proxyThe proxy is now available at http://localhost:3000.
With Environment Variables
Section titled “With Environment Variables”Configure the proxy with environment variables:
docker run -p 3000:3000 \ -e ALLOWED_ORIGINS="https://app.pondpilot.io,http://localhost:5173" \ -e RATE_LIMIT_REQUESTS=100 \ -e MAX_FILE_SIZE_MB=1000 \ ghcr.io/pondpilot/cors-proxyDocker Compose
Section titled “Docker Compose”For production deployments, use Docker Compose:
services: cors-proxy: image: ghcr.io/pondpilot/cors-proxy ports: - "3000:3000" environment: - NODE_ENV=production - PORT=3000 - ALLOWED_ORIGINS=https://app.pondpilot.io,https://yourapp.com - RATE_LIMIT_REQUESTS=60 - RATE_LIMIT_WINDOW_MS=60000 - MAX_FILE_SIZE_MB=500 - HTTPS_ONLY=true restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3Start with:
docker compose up -dCloudflare Workers Deployment
Section titled “Cloudflare Workers Deployment”Cloudflare Workers provides global edge deployment with automatic scaling and HTTPS.
Prerequisites
Section titled “Prerequisites”- Cloudflare account
- Wrangler CLI installed
- Clone the repository:
git clone https://github.com/pondpilot/cors-proxy.gitcd cors-proxy/cloudflare-workernpm install- Login to Cloudflare:
npx wrangler login- Configure
wrangler.toml:
name = "cors-proxy"main = "src/worker.ts"compatibility_date = "2024-01-01"
[env.production]vars = { ALLOWED_ORIGINS = "https://yourapp.com", RATE_LIMIT_REQUESTS = "60", MAX_FILE_SIZE_MB = "500"}- Deploy:
# Deploy to productionnpm run deploy:production
# Or deploy to workers.dev subdomainnpx wrangler deployCustom Domain
Section titled “Custom Domain”To use a custom domain instead of *.workers.dev:
- Add the domain to your Cloudflare account
- Update
wrangler.toml:
[env.production]routes = [ { pattern = "cors-proxy.yourdomain.com/*", zone_name = "yourdomain.com" }]- Deploy and configure DNS to point to Cloudflare.
Node.js Deployment
Section titled “Node.js Deployment”For integration into existing Node.js applications:
Installation
Section titled “Installation”git clone https://github.com/pondpilot/cors-proxy.gitcd cors-proxy/self-hostednpm installDevelopment
Section titled “Development”npm run devProduction Build
Section titled “Production Build”npm run buildnpm startAs Express Middleware
Section titled “As Express Middleware”You can integrate the proxy into an existing Express app:
import express from 'express';import { createProxyMiddleware } from './cors-proxy';
const app = express();
// Mount CORS proxy at /proxyapp.use('/proxy', createProxyMiddleware({ allowedOrigins: ['https://yourapp.com'], allowedDomains: ['*.s3.amazonaws.com', '*.cloudfront.net'], rateLimit: { requests: 60, windowMs: 60000 }}));
app.listen(3000);Configuration Reference
Section titled “Configuration Reference”Environment Variables
Section titled “Environment Variables”| Variable | Default | Description |
|---|---|---|
PORT | 3000 | Server port |
NODE_ENV | development | Environment (production enables stricter security) |
ALLOWED_ORIGINS | * | Comma-separated origins allowed to use the proxy |
ALLOWED_DOMAINS | (see below) | Comma-separated domains that can be proxied |
RATE_LIMIT_REQUESTS | 60 | Requests per IP per window |
RATE_LIMIT_WINDOW_MS | 60000 | Rate limit window in milliseconds |
MAX_FILE_SIZE_MB | 500 | Maximum response size in MB |
REQUEST_TIMEOUT_MS | 30000 | Request timeout in milliseconds |
HTTPS_ONLY | true (prod) | Only allow HTTPS target URLs |
ALLOW_CREDENTIALS | false | Forward Authorization headers (security risk) |
Default Allowed Domains
Section titled “Default Allowed Domains”When ALLOWED_DOMAINS is not set, the proxy allows these domains:
- AWS S3:
*.s3.amazonaws.com,*.s3.*.amazonaws.com - CloudFront:
*.cloudfront.net - GitHub:
*.github.io,*.githubusercontent.com - Google Cloud Storage:
*.storage.googleapis.com - Azure Blob Storage:
*.blob.core.windows.net - Public Data Portals:
data.gov,data.gouv.fr - DuckDB:
blobs.duckdb.org,*.duckdb.org
To customize, set ALLOWED_DOMAINS with your own list.
Domain Wildcards
Section titled “Domain Wildcards”Domain patterns support single-level wildcards:
| Pattern | Matches | Does Not Match |
|---|---|---|
*.example.com | api.example.com, cdn.example.com | a.b.example.com |
*.*.example.com | a.b.example.com | x.y.z.example.com |
example.com | example.com only | sub.example.com |
HTTPS Configuration
Section titled “HTTPS Configuration”With Caddy (Recommended)
Section titled “With Caddy (Recommended)”Caddy provides automatic HTTPS with Let’s Encrypt:
# Caddyfilecors-proxy.yourdomain.com { reverse_proxy localhost:3000}caddy runWith nginx
Section titled “With nginx”server { listen 443 ssl; server_name cors-proxy.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/cors-proxy.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/cors-proxy.yourdomain.com/privkey.pem;
location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }}Cloud Platform Deployment
Section titled “Cloud Platform Deployment”Railway
Section titled “Railway”One-click deployment:
railway initrailway upFly.io
Section titled “Fly.io”fly launchfly deployGoogle Cloud Run
Section titled “Google Cloud Run”# Build and push containergcloud builds submit --tag gcr.io/PROJECT_ID/cors-proxy
# Deploygcloud run deploy cors-proxy \ --image gcr.io/PROJECT_ID/cors-proxy \ --platform managed \ --allow-unauthenticatedHealth Checks
Section titled “Health Checks”The proxy exposes health check endpoints for monitoring:
| Endpoint | Response |
|---|---|
/health | {"status":"ok","service":"pondpilot-cors-proxy","uptime":...} |
/info | Service version and configuration |
Example health check:
curl http://localhost:3000/healthProduction Checklist
Section titled “Production Checklist”Before deploying to production:
- Set
NODE_ENV=production - Configure
ALLOWED_ORIGINS(no wildcards) - Review
ALLOWED_DOMAINSfor your use case - Enable HTTPS via reverse proxy or Cloudflare
- Set appropriate rate limits
- Configure health check monitoring
- Set up logging/alerting for errors
- Test SSRF protection (see Security)
Configuring PondPilot
Section titled “Configuring PondPilot”After deploying your self-hosted proxy, configure PondPilot to use it:
- Open PondPilot settings
- Navigate to Data Sources → CORS Proxy
- Enter your proxy URL (e.g.,
https://cors-proxy.yourcompany.com) - Save settings
PondPilot will now route remote file requests through your proxy.