{"id":30117392,"url":"https://github.com/longcipher/cf-proxy","last_synced_at":"2025-08-10T10:42:22.553Z","repository":{"id":307951491,"uuid":"1031171811","full_name":"longcipher/cf-proxy","owner":"longcipher","description":null,"archived":false,"fork":false,"pushed_at":"2025-08-03T07:19:12.000Z","size":42,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-03T09:11:35.768Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/longcipher.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-03T06:57:49.000Z","updated_at":"2025-08-03T07:19:15.000Z","dependencies_parsed_at":"2025-08-03T09:11:51.361Z","dependency_job_id":"fef73482-4c7a-4102-a133-586b7c9920e6","html_url":"https://github.com/longcipher/cf-proxy","commit_stats":null,"previous_names":["longcipher/cf-proxy"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/longcipher/cf-proxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longcipher%2Fcf-proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longcipher%2Fcf-proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longcipher%2Fcf-proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longcipher%2Fcf-proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/longcipher","download_url":"https://codeload.github.com/longcipher/cf-proxy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/longcipher%2Fcf-proxy/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269712986,"owners_count":24463216,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-10T10:42:19.490Z","updated_at":"2025-08-10T10:42:22.509Z","avatar_url":"https://github.com/longcipher.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cloudflare Workers Rust Reverse Proxy\n\nA feature-complete, configurable reverse proxy built with Rust, running on the Cloudflare Workers platform.\n\n## Features\n\n### 🚀 Core Features\n- **Multi-backend Support** - Configure multiple backend servers\n- **Load Balancing** - Round-robin, random, least connections strategies\n- **Health Checks** - Automatic backend server health monitoring\n- **Failover** - Automatic switching to healthy backend servers\n- **URL Path Proxy** - Direct URL proxying via path (e.g., `https://your-worker.com/https://example.com/`)\n\n### 🛡️ Security Features\n- **Access Control** - IP, country, and User-Agent based access control\n- **Security Headers** - Automatic security-related HTTP headers\n- **Request Validation** - HMAC signature verification support\n- **CORS Support** - Cross-Origin Resource Sharing headers for cross-domain requests\n\n### ⚡ Performance Optimization\n- **Smart Caching** - Cloudflare KV-based response caching\n- **Path Rewriting** - Regular expression-based path rewrite rules\n- **Header Processing** - Flexible request/response header modification\n- **Redirect Handling** - Automatic redirect processing with proper path resolution\n\n### 📊 Monitoring \u0026 Observability\n- **Real-time Metrics** - Request counts, error rates, response time statistics\n- **Health Check Endpoints** - Built-in health checks and status monitoring\n- **Detailed Logging** - Structured logging\n\n## Quick Start\n\n### 1. Environment Setup\n\nEnsure you have the following tools installed:\n\n- [Rust](https://rustup.rs/) (latest stable version)\n- [Node.js](https://nodejs.org/) and npm\n- [wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)\n\n```bash\n# Install Rust wasm32 target\nrustup target add wasm32-unknown-unknown\n\n# Install cargo-generate\ncargo install cargo-generate\n\n# Install wrangler globally\nnpm install -g wrangler\n```\n\n### 2. Usage Methods\n\nThis reverse proxy supports two main usage patterns:\n\n#### Method 1: URL Path Proxy (Direct Proxying)\n\nSimply make requests to your Cloudflare Workers URL with the target URL in the path:\n\n```bash\n# Proxy to example.com\nhttps://your-worker-url.com/https://example.com/\n\n# Proxy to specific path\nhttps://your-worker-url.com/https://api.example.com/users/123\n\n# Proxy with query parameters\nhttps://your-worker-url.com/https://api.example.com/search?q=test\u0026page=1\n```\n\n**Example requests:**\n```bash\ncurl https://your-worker.workers.dev/https://httpbin.org/json\ncurl https://your-worker.workers.dev/https://api.github.com/users/octocat\n```\n\n#### Method 2: Load-Balanced Backend Proxy\n\nConfigure multiple backend servers for load balancing and health checks. This method requires configuration through environment variables.\n\n### 3. Project Configuration\n\nEdit the `wrangler.toml` file:\n\n```toml\nname = \"my-cf-proxy\"\nmain = \"build/worker/shim.mjs\"\ncompatibility_date = \"2025-08-03\"\n\n[build]\ncommand = \"cargo install -q worker-build \u0026\u0026 worker-build --release\"\n\n# Environment variable configuration\n[env.production.vars]\nBACKEND_URLS = '[\"https://api1.example.com\", \"https://api2.example.com\"]'\nLOAD_BALANCER_STRATEGY = \"round_robin\"\nHEALTH_CHECK_ENABLED = \"true\"\nCACHE_ENABLED = \"true\"\nCACHE_TTL = \"300\"\n\n# KV namespace configuration\n[[kv_namespaces]]\nbinding = \"PROXY_KV\"\nid = \"your-kv-namespace-id\"\npreview_id = \"your-preview-kv-namespace-id\"\n```\n\n### Example: Proxy a custom domain to a Notion site\n\nUse this Worker to proxy your domain to a public Notion site (e.g., akjong.com → akagi201.notion.site).\n\n1. Configure wrangler (production example):\n\n```toml\nname = \"cf-proxy\"\nmain = \"build/worker/shim.mjs\"\ncompatibility_date = \"2025-08-03\"\n\n[build]\ncommand = \"cargo install -q worker-build \u0026\u0026 worker-build --release\"\n\n[env.production.vars]\nBACKEND_URLS = '[\"https://akagi201.notion.site\"]'\nHEALTH_CHECK_ENABLED = \"false\" # Notion doesn’t expose a /health endpoint you control\n\n# Optional KV if you enable cache\n# [[kv_namespaces]]\n# binding = \"PROXY_KV\"\n# id = \"your-kv-namespace-id\"\n\n# Optional (can be configured in CF Dashboard):\n# routes = [\n#   { pattern = \"akjong.com/*\", zone_name = \"akjong.com\" },\n#   { pattern = \"www.akjong.com/*\", zone_name = \"akjong.com\" }\n# ]\n```\n\n1. In Cloudflare Dashboard:\n\n- Ensure the akjong.com zone is on Cloudflare (nameservers set).\n- Workers → Routes: add `akjong.com/*` (and `www.akjong.com/*` if needed) to this Worker.\n- DNS records can remain standard; HTTP traffic will be intercepted by Routes.\n\n1. Verify:\n\n- Open \u003chttps://akjong.com\u003e and it will proxy to \u003chttps://akagi201.notion.site\u003e with matching paths.\n- Example: \u003chttps://akjong.com/xyz\u003e → \u003chttps://akagi201.notion.site/xyz\u003e\n\nNotes for Notion:\n\n- This is a transparent reverse proxy; it does not rewrite HTML. Absolute links may still point to akagi201.notion.site. If you want full domain substitution, you’ll need response body rewriting (not currently built-in).\n- CORS headers are permissive by default; regular website browsing needs no extra config.\n\n### 3. Local Development\n\n```bash\n# Start local development server\nwrangler dev\n\n# Access in browser\n# http://localhost:8787\n```\n\n### 4. Deploy to Production\n\n```bash\n# Deploy to Cloudflare Workers\nwrangler deploy\n```\n\n## Key Features Detail\n\n## URL Path Proxy\n\nThis feature allows you to use your Cloudflare Worker as a transparent proxy by embedding the target URL directly in the request path.\n\n### Usage Pattern\n\nMake requests to your Worker URL with the target URL appended to the path:\n\n```text\nhttps://your-worker-url.com/https://example.com/path/to/resource\n```\n\n### Examples\n\n1. **Basic usage:**\n\n   ```http\n   GET https://your-worker.example.workers.dev/https://api.github.com/users/octocat\n   ```\n\n2. **With query parameters:**\n\n   ```http\n   GET https://your-worker.example.workers.dev/https://httpbin.org/get?param1=value1\u0026param2=value2\n   ```\n\n3. **POST requests with data:**\n\n   ```http\n   POST https://your-worker.example.workers.dev/https://api.example.com/data\n   Content-Type: application/json\n   \n   {\"key\": \"value\"}\n   ```\n\n4. **Cross-origin requests from browser:**\n\n   ```javascript\n   fetch('https://your-worker.example.workers.dev/https://api.example.com/data', {\n     method: 'POST',\n     headers: {\n       'Content-Type': 'application/json',\n     },\n     body: JSON.stringify({key: 'value'})\n   })\n   ```\n\n### Proxy Features\n\n- **Automatic redirect handling**: Follows HTTP redirects and proxies to the final destination\n- **CORS support**: Adds proper CORS headers for cross-origin requests\n- **Method preservation**: Maintains original HTTP methods (GET, POST, PUT, DELETE, etc.)\n- **Headers forwarding**: Preserves original request headers (with necessary security filtering)\n- **Binary content support**: Handles all content types including images, files, etc.\n- **Query parameter preservation**: Maintains all query parameters from original request\n- **OPTIONS preflight handling**: Automatically handles CORS preflight requests\n\n### CORS Headers\n\nThe proxy automatically adds the following CORS headers:\n\n- `Access-Control-Allow-Origin: *`\n- `Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS`\n- `Access-Control-Allow-Headers: *`\n- `Access-Control-Allow-Credentials: true`\n- `Access-Control-Max-Age: 86400`\n\n### How It Works\n\n1. **URL Extraction**: The Worker extracts the target URL from the request path\n2. **Request Forwarding**: Creates a new request to the target URL with original headers and body\n3. **Redirect Handling**: If the response is a redirect (3xx), follows the redirect automatically\n4. **Response Processing**: Returns the final response with appropriate CORS headers\n5. **Error Handling**: Provides meaningful error messages for invalid URLs or failed requests\n\n### Security Considerations\n\n- The URL proxy feature is designed for development and testing purposes\n- Be mindful of potential SSRF (Server-Side Request Forgery) vulnerabilities\n- Consider implementing URL allowlists for production use\n- Review and filter sensitive headers before forwarding requests\n- Monitor usage to prevent abuse or excessive bandwidth consumption\n\n### Cross-Origin Resource Sharing (CORS)\n\nThe reverse proxy automatically adds CORS headers to allow cross-domain requests:\n\n```http\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH\nAccess-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With, Accept, Origin, User-Agent, DNT, Cache-Control, X-Mx-ReqToken, Keep-Alive, X-Requested-With, If-Modified-Since\nAccess-Control-Max-Age: 86400\nAccess-Control-Allow-Credentials: true\n```\n\nThis enables frontend JavaScript applications to make requests across different domains without CORS restrictions.\n\n### Redirect Processing\n\nThe proxy intelligently handles HTTP redirects (3xx status codes):\n\n- **Relative Redirects**: Converts relative Location headers to absolute URLs\n- **Absolute Redirects**: Preserves absolute redirect URLs\n- **Path-based Redirects**: Resolves relative paths against the original target URL\n\n## Configuration Options\n\n### Environment Variables\n\n| Variable | Type | Default | Description |\n|----------|------|---------|-------------|\n| `BACKEND_URLS` | JSON Array | `[\"https://httpbin.org\"]` | List of backend server URLs |\n| `LOAD_BALANCER_STRATEGY` | String | `\"round_robin\"` | Load balancing strategy |\n| `HEALTH_CHECK_ENABLED` | Boolean | `true` | Enable health checks |\n| `HEALTH_CHECK_INTERVAL` | Number | `30` | Health check interval (seconds) |\n| `CACHE_ENABLED` | Boolean | `false` | Enable caching |\n| `CACHE_TTL` | Number | `300` | Cache TTL (seconds) |\n| `CUSTOM_HEADERS` | JSON Object | `{}` | Custom request headers |\n| `ACCESS_RULES` | JSON Array | `[]` | Access control rules |\n\n### Load Balancing Strategies\n\n- `round_robin` - Round Robin (default)\n- `random` - Random selection\n- `least_connections` - Least connections\n- `weighted_round_robin` - Weighted round robin\n\n### Access Control Rules Example\n\n```json\n[\n  {\n    \"rule_type\": \"deny_ip\",\n    \"pattern\": \"192.168.1.100\"\n  },\n  {\n    \"rule_type\": \"allow_country\",\n    \"pattern\": \"US\"\n  },\n  {\n    \"rule_type\": \"deny_user_agent\",\n    \"pattern\": \".*bot.*\"\n  }\n]\n```\n\n### Path Rewrite Rules Example\n\n```json\n[\n  {\n    \"pattern\": \"^/api/v1/(.*)\",\n    \"replacement\": \"/v2/$1\"\n  },\n  {\n    \"pattern\": \"^/old-path/(.*)\",\n    \"replacement\": \"/new-path/$1\"\n  }\n]\n```\n\n## API Endpoints\n\n### Management Endpoints\n\n- `/_proxy/health` - Health check status\n- `/_proxy/stats` - Proxy statistics\n\n### Health Check Response Example\n\n```json\n{\n  \"status\": \"healthy\",\n  \"healthy_backends\": 2,\n  \"total_backends\": 2,\n  \"backends\": [\n    \"https://api1.example.com\",\n    \"https://api2.example.com\"\n  ],\n  \"timestamp\": \"2025-08-03T12:00:00Z\"\n}\n```\n\n### Statistics Response Example\n\n```json\n{\n  \"total_requests\": 1250,\n  \"total_errors\": 15,\n  \"error_rate\": \"1.20%\",\n  \"average_response_time\": \"125.50ms\",\n  \"cache_hits\": 450,\n  \"cache_misses\": 800,\n  \"cache_hit_rate\": \"36.00%\",\n  \"timestamp\": \"2025-08-03T12:00:00Z\"\n}\n```\n\n## Advanced Configuration\n\n### Complete wrangler.toml Example\n\n```toml\nname = \"advanced-cf-proxy\"\nmain = \"build/worker/shim.mjs\"\ncompatibility_date = \"2025-08-03\"\n\n[build]\ncommand = \"cargo install -q worker-build \u0026\u0026 worker-build --release\"\n\n[env.production.vars]\nBACKEND_URLS = '[\"https://api1.example.com\", \"https://api2.example.com\", \"https://api3.example.com\"]'\nLOAD_BALANCER_STRATEGY = \"round_robin\"\nHEALTH_CHECK_ENABLED = \"true\"\nHEALTH_CHECK_INTERVAL = \"30\"\nCACHE_ENABLED = \"true\"\nCACHE_TTL = \"600\"\nTIMEOUT = \"30\"\nRETRY_ATTEMPTS = \"3\"\n\nCUSTOM_HEADERS = '{\n  \"X-Custom-Header\": \"MyValue\",\n  \"X-API-Version\": \"v2\"\n}'\n\nACCESS_RULES = '[\n  {\"rule_type\": \"deny_country\", \"pattern\": \"CN\"},\n  {\"rule_type\": \"deny_user_agent\", \"pattern\": \".*crawler.*\"}\n]'\n\nPATH_REWRITE_RULES = '[\n  {\"pattern\": \"^/api/v1/(.*)\", \"replacement\": \"/v2/$1\"},\n  {\"pattern\": \"^/legacy/(.*)\", \"replacement\": \"/modern/$1\"}\n]'\n\n[[kv_namespaces]]\nbinding = \"PROXY_KV\"\nid = \"your-production-kv-id\"\n\n[env.staging.vars]\nBACKEND_URLS = '[\"https://staging-api.example.com\"]'\nCACHE_ENABLED = \"false\"\n\n[[env.staging.kv_namespaces]]\nbinding = \"PROXY_KV\"\nid = \"your-staging-kv-id\"\n```\n\n## Monitoring and Debugging\n\n### View Logs\n\n```bash\n# View Worker logs in real-time\nwrangler tail\n```\n\n### Performance Monitoring\n\nThe proxy automatically records the following metrics:\n\n- Total requests and error counts\n- Average response time\n- Cache hit rate\n- Backend health status\n\n### Debugging Tips\n\n1. **Enable verbose logging**: Set `LOG_LEVEL=debug`\n2. **Use health check endpoint**: Regularly check `/_proxy/health`\n3. **Monitor statistics**: Get performance data through `/_proxy/stats`\n\n## Best Practices\n\n### Security Recommendations\n\n1. **Use environment variables**: Manage sensitive configurations through environment variables or Workers Secrets\n2. **Enable access control**: Configure appropriate IP and geographic restrictions\n3. **Rotate keys regularly**: If using HMAC verification, update keys regularly\n\n### Performance Optimization\n\n1. **Configure caching appropriately**: Set suitable TTL based on content characteristics\n2. **Health check intervals**: Balance detection timeliness and resource consumption\n3. **Timeout settings**: Set appropriate request timeout values\n\n### Operations Recommendations\n\n1. **Monitoring alerts**: Set up monitoring alerts for key metrics\n2. **Failure response plans**: Prepare emergency response plans for backend service failures\n3. **Capacity planning**: Plan backend capacity based on traffic growth\n\n## Troubleshooting\n\n### Common Issues\n\n1. **502 Bad Gateway**\n   - Check if backend servers are running normally\n   - Verify backend URL configuration is correct\n   - Check health check status\n\n2. **Request Timeout**\n   - Adjust `TIMEOUT` configuration\n   - Check network connectivity\n   - Optimize backend service response time\n\n3. **Cache Issues**\n   - Verify KV namespace configuration\n   - Check cache policy settings\n   - Clear cache and retest\n\n### Debug Commands\n\n```bash\n# Check configuration\nwrangler whoami\nwrangler kv:namespace list\n\n# View KV storage\nwrangler kv:key list --binding PROXY_KV\n\n# Deploy before validation\nwrangler deploy --dry-run\n```\n\n## Contributing\n\nWelcome to submit Issues and Pull Requests!\n\n### Development Workflow\n\n1. Fork this repository\n2. Create feature branch\n3. Submit changes\n4. Run tests\n5. Submit Pull Request\n\n### Code Standards\n\n- Use `cargo fmt` to format code\n- Use `cargo clippy` to check code quality\n- Add appropriate test coverage\n\n## License\n\nThis project uses the MIT license. See [LICENSE](LICENSE) file for details.\n\n## Related Links\n\n- [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/)\n- [workers-rs Project](https://github.com/cloudflare/workers-rs)\n- [Wrangler CLI Documentation](https://developers.cloudflare.com/workers/wrangler/)\n\n## Known limitations and gaps (implementation review)\n\nThis section highlights current behavior vs. documentation and areas to improve:\n\n- Request middleware may drop bodies: `apply_request_middleware` rebuilds a Request without copying the body for non-GET/HEAD methods. The body is copied later in proxying, but if middleware runs first and rebuilds, the original body can be lost. Fix by preserving the body or deferring header additions to the proxy request step only.\n- Load balancer strategy is not applied: `LoadBalancer::new` always initializes with RoundRobin and ignores the configured strategy. Random/Least/Weighted variants are placeholders that fall back to round-robin.\n- Health checks are simplistic: No per-backend configurable health path is used, and checks aren’t actively performed; backends are marked unhealthy only on request errors with a cooldown window.\n- CORS spec mismatch: Responses set `Access-Control-Allow-Origin: *` and `Access-Control-Allow-Credentials: true` together, which browsers do not allow. Consider echoing the Origin instead when credentials are needed, or set credentials to false.\n- Security headers: Uses legacy `X-XSS-Protection`; consider removing or replacing with modern protections (CSP, etc.).\n- HMAC verification utility is incorrect: `verify_hmac_sha256` computes a plain SHA-256 of `secret + data`, not a true HMAC. Replace with the `hmac` crate and constant-time comparison.\n- Access rule logic is basic: `allow_country` currently denies all non-matching requests unconditionally and doesn’t support combined allowlists/denylists robustly.\n- Redirect handling in URL-proxy mode doesn’t rewrite to the Worker domain; absolute redirects remain as-is. This is fine for transparency but note the behavior.\n- Weighted/least-connections metrics are not tracked: No per-backend connection counters or weights are currently honored.\n\nSuggested next steps (low risk):\n\n- Remove header mutation from request middleware and only apply headers in `create_proxy_request`, preserving bodies.\n- Honor `LOAD_BALANCER_STRATEGY` and implement at least true random selection using `js_sys::Date::now()` or a simple PRNG suitable for WASM.\n- Replace `verify_hmac_sha256` with a proper HMAC-SHA256.\n- Adjust CORS to echo Origin when `credentials` are required.\n- Expose a configurable health path per backend or a global `HEALTH_CHECK_PATH` and implement periodic checks.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flongcipher%2Fcf-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flongcipher%2Fcf-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flongcipher%2Fcf-proxy/lists"}