A 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.
- Features
- Quick Start
- Deployment
- Configuration
- API Reference
- Signed URLs
- Cache Management
- Security Notes
- Roadmap
- Contributing
- License
- Edge‑level Authentication – Optional signed URLs with expiration.
- Range Requests & Partial Responses – Stream video, large files, and resume interrupted downloads.
- Advanced Cache Management – Intelligent caching with TTL control, metrics, and purging.
- Automatic Path Normalisation – Protection against directory traversal.
- Flexible CORS – Fine‑grained per‑origin allow‑list via environment variable.
- Health Check endpoint.
- Built with Hono on Cloudflare Workers for minimal cold starts.
# Prerequisites: Node.js ≥ 18, Wrangler ≥ 3, a Cloudflare account
# 1. Install dependencies
npm install
# 2. Copy the sample env file and update values
cp .dev.vars.template .dev.vars
# 3. Start a local dev server
npm run dev
# Deploy to your Cloudflare account authenticated with `wrangler login`
npm run deploy
Ensure wrangler.toml
contains the [vars] block below.
Variable | Description | Example | Required |
---|---|---|---|
END_POINT |
S3 API endpoint | https://s3.us-west-1.amazonaws.com |
Yes |
ACCESS_KEY |
S3 access key ID | AKIAEXAMPLE |
Yes |
SECRET_KEY |
S3 secret access key | wJalrX... |
Yes |
BUCKET_NAME |
S3 bucket to proxy | my-assets |
Yes |
S3_REGION |
AWS region for S3 operations | us-west-1 |
Yes |
RANGE_RETRY_ATTEMPTS |
Max retries for range requests | 3 |
Yes |
URL_SIGNING_SECRET |
HMAC secret for signed URLs | super-secret-32-chars-minimum |
No |
CORS_ALLOW_ORIGINS |
Comma-separated CORS origins | https://example.com,https://app.example.com |
No |
VERSION |
Version identifier | v1.0.0 |
No |
CACHE_ENABLED |
Enable caching system | true |
No |
CACHE_TTL_SECONDS |
Default cache TTL (1-604800) | 3600 |
No |
CACHE_DEBUG |
Enable cache debug headers | false |
No |
CACHE_MIN_TTL_SECONDS |
Minimum cache TTL (1-86400) | 60 |
No |
CACHE_MAX_TTL_SECONDS |
Maximum cache TTL (60-604800) | 86400 |
No |
CACHE_OVERRIDE_S3_HEADERS |
Override S3 cache headers | false |
No |
CACHE_PURGE_SECRET |
Secret for cache purge operations | your-secure-secret |
No |
ENFORCE_URL_SIGNING |
Require URL signing for all requests | false |
No |
URL_SIGNING_REQUIRED_PATHS |
Comma-separated paths requiring signing | /private,/secure |
No |
ENABLE_LIST_ENDPOINT |
Enable directory listing endpoint | true |
No |
ENABLE_UPLOAD_ENDPOINT |
Enable file upload endpoints | true |
No |
ENABLE_DELETE_ENDPOINT |
Enable file deletion endpoints | true |
No |
PREFIX_MAX_LENGTH |
Maximum prefix length (1-1024) | 256 |
No |
PREFIX_MAX_DEPTH |
Maximum prefix depth (1-50) | 10 |
No |
Example wrangler.jsonc
snippet:
Returns the object keys directly under the given prefix.
{
"keys": ["images/a.jpg", "images/b.png"]
}
Fetches the object as‑is. Supports HTTP Range
headers.
download
– forcesContent‑Disposition: attachment
(optionally override filename).inline
– forcesContent‑Disposition: inline
.
Examples:
GET /images/a.jpg
GET /images/a.jpg?download
GET /images/a.jpg?download=foo.png
GET /images/a.jpg?inline
Uploads 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.
If URL signing is enabled, PUT requests must be signed.
Generates a presigned S3 URL for PUT uploads. Request body:
{
"key": "path/to/your/object.txt",
"expiresIn": 3600,
// Optional: expiration in seconds (default 3600, max 7 days)
"conditions": {
// Optional: conditions for the upload
"contentType": "text/plain",
"contentLength": 1024,
// in bytes
"contentMd5": "base64-md5-hash",
"metadata": {
"custom-key": "custom-value"
}
}
}
Response:
{
"presignedUrl": "...",
"key": "path/to/your/object.txt",
"expiresIn": 3600,
"expiresAt": "...",
"method": "PUT",
"requiredHeaders": {
...
}
}
Note: Multipart upload functionality is currently being enhanced. Complete documentation will be available soon.
Initiates a multipart upload.
Uploads a part of a multipart upload.
Completes a multipart upload.
Aborts a multipart upload.
Detailed documentation for multipart upload workflows, XML formats, and examples will be added in the next update.
Deletes a file. An optionalversionId
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.
Batch deletes files. Request body:
{
"keys": ["path/to/object1.txt", "path/to/object2.jpg"],
"quiet": false
// Optional: if true, S3 returns success even if some keys fail (default false)
}
A maximum of 1000 keys can be specified. The S3 XML response detailing the result for each key is returned (unlessquiet
is true
). If URL signing is enabled, this endpoint can be protected.
Returns build version and server time. Useful for liveness probes.
Returns detailed cache statistics including hit rates, configuration, and performance metrics.
{
"config": {
"enabled": true,
"ttlSeconds": 3600,
"overrideS3Headers": false
// ... more config ...
},
"metrics": {
"cacheHits": 1250,
"cacheMisses": 180
// ... more metrics ...
},
"performance": {
// ... performance data ...
}
}
Returns the health status of the cache system.
{
"status": "healthy",
"message": "Cache is functioning normally"
// ... more details ...
}
Authenticated endpoint for cache invalidation. Requires Authorization: Bearer <CACHE_PURGE_SECRET>
header.
Purge by specific keys:
curl -X POST /__cache/purge \
-H "Authorization: Bearer your-secure-secret" \
-H "Content-Type: application/json" \
-d '{
"keys": ["/path/to/file1.jpg", "/path/to/file2.pdf"]
}'
Purge by pattern (future support):
curl -X POST /__cache/purge \
-H "Authorization: Bearer your-secure-secret" \
-H "Content-Type: application/json" \
-d '{
"pattern": "^/images/.*\.jpg$"
}'
Authenticated endpoint for proactive cache population. Requires Authorization: Bearer <CACHE_PURGE_SECRET>
header.
curl -X POST /__cache/warm \
-H "Authorization: Bearer your-secure-secret" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://your-worker.example.com/popular-file1.jpg",
"https://your-worker.example.com/popular-file2.pdf"
]
}'
URL signing provides secure, time-limited access to files. Configure the URL_SIGNING_SECRET
environment variable to enable this feature.
Signed URLs include:
exp
: Expiration timestampsig
: HMAC-SHA256 signature
Example signed URL format:
https://worker.example.com/private/report.pdf?exp=1710000000&sig=abcdef...
Refer to the documentation for URL signing implementation details and security best practices.
This proxy includes a significantly enhanced cache management system that provides better performance, more control, and comprehensive cache operations. Key features include:
- Hybrid Caching Strategy: Combines Cloudflare's edge caching with the Cache API.
- Enhanced Conditional Request Handling: ETag and Last-Modified support.
- Advanced Cache Invalidation & Management: Selective and pattern-based purging, cache warming.
- Advanced Analytics & Debugging: Detailed metrics, debug headers, and real-time statistics.
GET /__cache/stats
– Detailed cache statistics and performance metrics.GET /__cache/health
– Health status of the cache system.POST /__cache/purge
– Authenticated cache invalidation endpoint.POST /__cache/warm
– Authenticated endpoint for proactive cache population.
For complete documentation on configuration, best practices, and troubleshooting, see docs/cache-management.md.
This proxy implements comprehensive security features for production use, including:
- Enhanced URL Signature Validation
- Path Traversal Protection
- Cache Security
- Constant-Time Verification
- Appropriate Error Codes
- CORS Protection
For complete security documentation, implementation details, and best practices, see docs/security.md.
- Object upload endpoint
- ETag‑aware caching
- Terraform module for one‑command deploy
Pull requests are welcome! Please open an issue first to discuss major changes.
MIT © 2025 Shogo Ishigami
Found a bug? Have a question? Open an issue and we'll take a look.