Skip to content

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.

OptionBest ForEffort
DockerQuick local/server deploymentLow
Docker ComposeProduction server deploymentLow
Cloudflare WorkersGlobal edge deployment, serverlessMedium
Node.jsCustom integrationsMedium

Run the proxy with Docker:

Terminal window
docker run -p 3000:3000 ghcr.io/pondpilot/cors-proxy

The proxy is now available at http://localhost:3000.

Configure the proxy with environment variables:

Terminal window
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-proxy

For production deployments, use Docker Compose:

docker-compose.yml
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: 3

Start with:

Terminal window
docker compose up -d

Cloudflare Workers provides global edge deployment with automatic scaling and HTTPS.

  • Cloudflare account
  • Wrangler CLI installed
  1. Clone the repository:
Terminal window
git clone https://github.com/pondpilot/cors-proxy.git
cd cors-proxy/cloudflare-worker
npm install
  1. Login to Cloudflare:
Terminal window
npx wrangler login
  1. 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"
}
  1. Deploy:
Terminal window
# Deploy to production
npm run deploy:production
# Or deploy to workers.dev subdomain
npx wrangler deploy

To use a custom domain instead of *.workers.dev:

  1. Add the domain to your Cloudflare account
  2. Update wrangler.toml:
[env.production]
routes = [
{ pattern = "cors-proxy.yourdomain.com/*", zone_name = "yourdomain.com" }
]
  1. Deploy and configure DNS to point to Cloudflare.

For integration into existing Node.js applications:

Terminal window
git clone https://github.com/pondpilot/cors-proxy.git
cd cors-proxy/self-hosted
npm install
Terminal window
npm run dev
Terminal window
npm run build
npm start

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 /proxy
app.use('/proxy', createProxyMiddleware({
allowedOrigins: ['https://yourapp.com'],
allowedDomains: ['*.s3.amazonaws.com', '*.cloudfront.net'],
rateLimit: { requests: 60, windowMs: 60000 }
}));
app.listen(3000);
VariableDefaultDescription
PORT3000Server port
NODE_ENVdevelopmentEnvironment (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_REQUESTS60Requests per IP per window
RATE_LIMIT_WINDOW_MS60000Rate limit window in milliseconds
MAX_FILE_SIZE_MB500Maximum response size in MB
REQUEST_TIMEOUT_MS30000Request timeout in milliseconds
HTTPS_ONLYtrue (prod)Only allow HTTPS target URLs
ALLOW_CREDENTIALSfalseForward Authorization headers (security risk)

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 patterns support single-level wildcards:

PatternMatchesDoes Not Match
*.example.comapi.example.com, cdn.example.coma.b.example.com
*.*.example.coma.b.example.comx.y.z.example.com
example.comexample.com onlysub.example.com

Caddy provides automatic HTTPS with Let’s Encrypt:

# Caddyfile
cors-proxy.yourdomain.com {
reverse_proxy localhost:3000
}
Terminal window
caddy run
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;
}
}

One-click deployment:

Terminal window
railway init
railway up
Terminal window
fly launch
fly deploy
Terminal window
# Build and push container
gcloud builds submit --tag gcr.io/PROJECT_ID/cors-proxy
# Deploy
gcloud run deploy cors-proxy \
--image gcr.io/PROJECT_ID/cors-proxy \
--platform managed \
--allow-unauthenticated

The proxy exposes health check endpoints for monitoring:

EndpointResponse
/health{"status":"ok","service":"pondpilot-cors-proxy","uptime":...}
/infoService version and configuration

Example health check:

Terminal window
curl http://localhost:3000/health

Before deploying to production:

  • Set NODE_ENV=production
  • Configure ALLOWED_ORIGINS (no wildcards)
  • Review ALLOWED_DOMAINS for 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)

After deploying your self-hosted proxy, configure PondPilot to use it:

  1. Open PondPilot settings
  2. Navigate to Data SourcesCORS Proxy
  3. Enter your proxy URL (e.g., https://cors-proxy.yourcompany.com)
  4. Save settings

PondPilot will now route remote file requests through your proxy.