{"id":28894967,"url":"https://github.com/nusu-github/cf-workers-s3-proxy","last_synced_at":"2026-03-16T01:38:58.695Z","repository":{"id":238429705,"uuid":"796531997","full_name":"nusu-github/cf-workers-s3-proxy","owner":"nusu-github","description":"s3(\u0026compatible) proxy to run on Cloudflare Workers.","archived":false,"fork":false,"pushed_at":"2025-11-21T23:47:25.000Z","size":146,"stargazers_count":1,"open_issues_count":6,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-02-09T04:32:10.113Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nusu-github.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-05-06T06:12:13.000Z","updated_at":"2025-06-17T11:55:13.000Z","dependencies_parsed_at":"2025-01-25T07:23:10.453Z","dependency_job_id":"a8acd67d-c800-4bda-8b04-fed06882a891","html_url":"https://github.com/nusu-github/cf-workers-s3-proxy","commit_stats":null,"previous_names":["nusu-github/cf-workers-s3-proxy"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nusu-github/cf-workers-s3-proxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nusu-github%2Fcf-workers-s3-proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nusu-github%2Fcf-workers-s3-proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nusu-github%2Fcf-workers-s3-proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nusu-github%2Fcf-workers-s3-proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nusu-github","download_url":"https://codeload.github.com/nusu-github/cf-workers-s3-proxy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nusu-github%2Fcf-workers-s3-proxy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30558211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-15T23:30:23.986Z","status":"ssl_error","status_checked_at":"2026-03-15T23:28:43.564Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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-06-21T04:15:48.862Z","updated_at":"2026-03-16T01:38:58.663Z","avatar_url":"https://github.com/nusu-github.png","language":"TypeScript","readme":"# Cloudflare Workers S3 Proxy\n\n![Cloudflare Workers](https://img.shields.io/badge/cloudflare%20workers-F38020?style=for-the-badge\u0026logo=cloudflare) ![Hono](https://img.shields.io/badge/hono-E36002?style=for-the-badge\u0026logo=hono) ![MIT License](https://img.shields.io/badge/license-MIT-green?style=for-the-badge)\n\nA lightweight, cache‑friendly proxy that lets you serve objects from any S3‑compatible storage (Backblaze B2, MinIO,DigitalOcean Spaces, etc.) through the Cloudflare edge. Perfect for static asset hosting, private downloads, or as adrop‑in CDN for existing buckets.\n\n## Table of Contents\n\n- [Features](#features)\n- [Quick Start](#quick-start)\n- [Deployment](#deployment)\n- [Configuration](#configuration)\n- [API Reference](#api-reference)\n- [Signed URLs](#signed-urls)\n- [Cache Management](#cache-management)\n- [Security Notes](#security-notes)\n- [Roadmap](#roadmap)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Features\n\n- **Edge‑level Authentication** – Optional signed URLs with expiration.\n- **Range Requests \u0026 Partial Responses** – Stream video, large files, and resume interrupted downloads.\n- **Advanced Cache Management** – Intelligent caching with TTL control, metrics, and purging.\n- **Automatic Path Normalisation** – Protection against directory traversal.\n- **Flexible CORS** – Fine‑grained per‑origin allow‑list via environment variable.\n- **Health Check** endpoint.\n- **Built with [Hono](https://honojs.dev) on Cloudflare Workers** for minimal cold starts.\n\n## Quick Start\n\n```bash\n# Prerequisites: Node.js ≥ 18, Wrangler ≥ 3, a Cloudflare account\n\n# 1. Install dependencies\nnpm install\n\n# 2. Copy the sample env file and update values\ncp .dev.vars.template .dev.vars\n\n# 3. Start a local dev server\nnpm run dev\n```\n\n## Deployment\n\n```bash\n# Deploy to your Cloudflare account authenticated with `wrangler login`\n\nnpm run deploy\n```\n\nEnsure `wrangler.toml` contains the **\\[vars]** block below.\n\n## Configuration\n\n| Variable                     | Description                             | Example                                       | Required |\n| ---------------------------- | --------------------------------------- | --------------------------------------------- | -------- |\n| `END_POINT`                  | S3 API endpoint                         | `https://s3.us-west-1.amazonaws.com`          | Yes      |\n| `ACCESS_KEY`                 | S3 access key ID                        | `AKIAEXAMPLE`                                 | Yes      |\n| `SECRET_KEY`                 | S3 secret access key                    | `wJalrX...`                                   | Yes      |\n| `BUCKET_NAME`                | S3 bucket to proxy                      | `my-assets`                                   | Yes      |\n| `S3_REGION`                  | AWS region for S3 operations            | `us-west-1`                                   | Yes      |\n| `RANGE_RETRY_ATTEMPTS`       | Max retries for range requests          | `3`                                           | Yes      |\n| `URL_SIGNING_SECRET`         | HMAC secret for signed URLs             | `super-secret-32-chars-minimum`               | No       |\n| `CORS_ALLOW_ORIGINS`         | Comma-separated CORS origins            | `https://example.com,https://app.example.com` | No       |\n| `VERSION`                    | Version identifier                      | `v1.0.0`                                      | No       |\n| `CACHE_ENABLED`              | Enable caching system                   | `true`                                        | No       |\n| `CACHE_TTL_SECONDS`          | Default cache TTL (1-604800)            | `3600`                                        | No       |\n| `CACHE_DEBUG`                | Enable cache debug headers              | `false`                                       | No       |\n| `CACHE_MIN_TTL_SECONDS`      | Minimum cache TTL (1-86400)             | `60`                                          | No       |\n| `CACHE_MAX_TTL_SECONDS`      | Maximum cache TTL (60-604800)           | `86400`                                       | No       |\n| `CACHE_OVERRIDE_S3_HEADERS`  | Override S3 cache headers               | `false`                                       | No       |\n| `CACHE_PURGE_SECRET`         | Secret for cache purge operations       | `your-secure-secret`                          | No       |\n| `ENFORCE_URL_SIGNING`        | Require URL signing for all requests    | `false`                                       | No       |\n| `URL_SIGNING_REQUIRED_PATHS` | Comma-separated paths requiring signing | `/private,/secure`                            | No       |\n| `ENABLE_LIST_ENDPOINT`       | Enable directory listing endpoint       | `true`                                        | No       |\n| `ENABLE_UPLOAD_ENDPOINT`     | Enable file upload endpoints            | `true`                                        | No       |\n| `ENABLE_DELETE_ENDPOINT`     | Enable file deletion endpoints          | `true`                                        | No       |\n| `PREFIX_MAX_LENGTH`          | Maximum prefix length (1-1024)          | `256`                                         | No       |\n| `PREFIX_MAX_DEPTH`           | Maximum prefix depth (1-50)             | `10`                                          | No       |\n\nExample `wrangler.jsonc` snippet:\n\n```jsonc\n{\n  \"name\": \"my-s3-proxy\",\n  \"main\": \"src/index.ts\",\n  \"compatibility_date\": \"2025-01-01\",\n  \"vars\": {\n    \"END_POINT\": \"https://s3.us-west-1.amazonaws.com\",\n    \"ACCESS_KEY\": \"AKIAEXAMPLE\",\n    \"SECRET_KEY\": \"wJalrX...\",\n    \"BUCKET_NAME\": \"my-assets\",\n    \"S3_REGION\": \"us-west-1\",\n    \"RANGE_RETRY_ATTEMPTS\": 3,\n    \"CORS_ALLOW_ORIGINS\": \"https://example.com\",\n    \"VERSION\": \"v1.0.0\",\n    \"CACHE_ENABLED\": true,\n    \"CACHE_TTL_SECONDS\": 3600,\n    \"CACHE_DEBUG\": false,\n    \"CACHE_MIN_TTL_SECONDS\": 60,\n    \"CACHE_MAX_TTL_SECONDS\": 86400,\n    \"CACHE_OVERRIDE_S3_HEADERS\": false,\n    \"CACHE_PURGE_SECRET\": \"your-secure-secret\",\n    \"ENABLE_LIST_ENDPOINT\": true,\n    \"ENABLE_UPLOAD_ENDPOINT\": true,\n    \"ENABLE_DELETE_ENDPOINT\": true\n  }\n}\n```\n\n## API Reference\n\n### `GET /list?prefix=\u003cpath/\u003e`\n\nReturns the object keys directly under the given prefix.\n\n```json\n{\n  \"keys\": [\"images/a.jpg\", \"images/b.png\"]\n}\n```\n\n### `GET /\u003ckey\u003e[?download[=name]|inline]`\n\nFetches the object as‑is. Supports HTTP `Range` headers.\n\n- `download` – forces `Content‑Disposition: attachment` (optionally override filename).\n- `inline` – forces `Content‑Disposition: inline`.\n\nExamples:\n\n```\nGET /images/a.jpg\nGET /images/a.jpg?download\nGET /images/a.jpg?download=foo.png\nGET /images/a.jpg?inline\n```\n\n### `PUT /\u003cfilename\u003e`\n\nUploads a file. The worker streams the request body directly to S3.Relevant headers like `Content-Type`, `Content-MD5`, `Content-Length`, `Content-Encoding`, `Cache-Control`,`x-amz-meta-*`, `x-amz-checksum-*`, and S3 server-side encryption headers are forwarded to S3.\nIf URL signing is enabled, PUT requests must be signed.\n\n### `POST /presigned-upload`\n\nGenerates a presigned S3 URL for PUT uploads.\nRequest body:\n\n```json\n{\n  \"key\": \"path/to/your/object.txt\",\n  \"expiresIn\": 3600,\n  // Optional: expiration in seconds (default 3600, max 7 days)\n  \"conditions\": {\n    // Optional: conditions for the upload\n    \"contentType\": \"text/plain\",\n    \"contentLength\": 1024,\n    // in bytes\n    \"contentMd5\": \"base64-md5-hash\",\n    \"metadata\": {\n      \"custom-key\": \"custom-value\"\n    }\n  }\n}\n```\n\nResponse:\n\n```json\n{\n  \"presignedUrl\": \"...\",\n  \"key\": \"path/to/your/object.txt\",\n  \"expiresIn\": 3600,\n  \"expiresAt\": \"...\",\n  \"method\": \"PUT\",\n  \"requiredHeaders\": {\n    ...\n  }\n}\n```\n\n### Multipart Upload Endpoints\n\n**Note**: Multipart upload functionality is currently being enhanced. Complete documentation will be available soon.\n\n#### `POST /\u003cfilename\u003e/uploads`\n\nInitiates a multipart upload.\n\n#### `PUT /\u003cfilename\u003e?partNumber=X\u0026uploadId=Y`\n\nUploads a part of a multipart upload.\n\n#### `POST /\u003cfilename\u003e?uploadId=Y`\n\nCompletes a multipart upload.\n\n#### `DELETE /\u003cfilename\u003e?uploadId=Y`\n\nAborts a multipart upload.\n\n_Detailed documentation for multipart upload workflows, XML formats, and examples will be added in the next update._\n\n### `DELETE /\u003cfilename\u003e`\n\nDeletes a file. An optional`versionId` query parameter can be included. Returns a 200 OK with a JSON body indicating success. If URL signing is enabled, DELETE requests must be signed.\n\n### `POST /delete`\n\nBatch deletes files.\nRequest body:\n\n```json\n{\n  \"keys\": [\"path/to/object1.txt\", \"path/to/object2.jpg\"],\n  \"quiet\": false\n  // Optional: if true, S3 returns success even if some keys fail (default false)\n}\n```\n\nA maximum of 1000 keys can be specified. The S3 XML response detailing the result for each key is returned (unless`quiet` is `true`). If URL signing is enabled, this endpoint can be protected.\n\n### `GET /__health`\n\nReturns build version and server time. Useful for liveness probes.\n\n### `GET /__cache/stats`\n\nReturns detailed cache statistics including hit rates, configuration, and performance metrics.\n\n```json\n{\n  \"config\": {\n    \"enabled\": true,\n    \"ttlSeconds\": 3600,\n    \"overrideS3Headers\": false\n    // ... more config ...\n  },\n  \"metrics\": {\n    \"cacheHits\": 1250,\n    \"cacheMisses\": 180\n    // ... more metrics ...\n  },\n  \"performance\": {\n    // ... performance data ...\n  }\n}\n```\n\n### `GET /__cache/health`\n\nReturns the health status of the cache system.\n\n```json\n{\n  \"status\": \"healthy\",\n  \"message\": \"Cache is functioning normally\"\n  // ... more details ...\n}\n```\n\n### `POST /__cache/purge`\n\nAuthenticated endpoint for cache invalidation. Requires `Authorization: Bearer \u003cCACHE_PURGE_SECRET\u003e` header.\n\nPurge by specific keys:\n\n```bash\ncurl -X POST /__cache/purge \\\n  -H \"Authorization: Bearer your-secure-secret\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"keys\": [\"/path/to/file1.jpg\", \"/path/to/file2.pdf\"]\n  }'\n```\n\nPurge by pattern (future support):\n\n```bash\ncurl -X POST /__cache/purge \\\n  -H \"Authorization: Bearer your-secure-secret\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"pattern\": \"^/images/.*\\.jpg$\"\n  }'\n```\n\n### `POST /__cache/warm`\n\nAuthenticated endpoint for proactive cache population. Requires `Authorization: Bearer \u003cCACHE_PURGE_SECRET\u003e` header.\n\n```bash\ncurl -X POST /__cache/warm \\\n  -H \"Authorization: Bearer your-secure-secret\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"urls\": [\n      \"https://your-worker.example.com/popular-file1.jpg\",\n      \"https://your-worker.example.com/popular-file2.pdf\"\n    ]\n  }'\n```\n\n## Signed URLs\n\nURL signing provides secure, time-limited access to files. Configure the `URL_SIGNING_SECRET` environment variable to enable this feature.\n\nSigned URLs include:\n\n- `exp`: Expiration timestamp\n- `sig`: HMAC-SHA256 signature\n\nExample signed URL format:\n\n```\nhttps://worker.example.com/private/report.pdf?exp=1710000000\u0026sig=abcdef...\n```\n\nRefer to the documentation for URL signing implementation details and security best practices.\n\n## Cache Management\n\nThis proxy includes a significantly enhanced cache management system that provides better performance, more control, and comprehensive cache operations. Key features include:\n\n- **Hybrid Caching Strategy**: Combines Cloudflare's edge caching with the Cache API.\n- **Enhanced Conditional Request Handling**: ETag and Last-Modified support.\n- **Advanced Cache Invalidation \u0026 Management**: Selective and pattern-based purging, cache warming.\n- **Advanced Analytics \u0026 Debugging**: Detailed metrics, debug headers, and real-time statistics.\n\n### Cache Endpoints\n\n- `GET /__cache/stats` – Detailed cache statistics and performance metrics.\n- `GET /__cache/health` – Health status of the cache system.\n- `POST /__cache/purge` – Authenticated cache invalidation endpoint.\n- `POST /__cache/warm` – Authenticated endpoint for proactive cache population.\n\nFor complete documentation on configuration, best practices, and troubleshooting, see **[docs/cache-management.md](./docs/cache-management.md)**.\n\n## Security Notes\n\nThis proxy implements comprehensive security features for production use, including:\n\n- **Enhanced URL Signature Validation**\n- **Path Traversal Protection**\n- **Cache Security**\n- **Constant-Time Verification**\n- **Appropriate Error Codes**\n- **CORS Protection**\n\nFor complete security documentation, implementation details, and best practices, see **[docs/security.md](./docs/security.md)**.\n\n## Roadmap\n\n- Object upload endpoint\n- ETag‑aware caching\n- Terraform module for one‑command deploy\n\n## Contributing\n\nPull requests are welcome! Please open an issue first to discuss major changes.\n\n## License\n\nMIT © 2025 Shogo Ishigami\n\n---\n\n### Contact\n\nFound a bug? Have a question? [Open an issue](https://github.com/nusu-github/cf-workers-s3-proxy/issues) and we'll take\na look.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnusu-github%2Fcf-workers-s3-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnusu-github%2Fcf-workers-s3-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnusu-github%2Fcf-workers-s3-proxy/lists"}