A modern file transfer system built on Cloudflare Workers + R2. Zero server cost, global CDN acceleration, CN/EN bilingual UI.
______ _ _ ______ _
| ____(_) | | ____| | |
| |__ _| | ___| |__ __ _ ___| |_
| __| | | |/ _ \ __/ _` / __| __|
| | | | | __/ | | (_| \__ \_ |
|_| |_|_|\___|_| \__,_|___/\__|
- Drag & Drop — Drop files onto the page or click to browse
- Multi-file upload — Upload multiple files at once
- Clipboard paste — Ctrl+V to paste images directly
- Text sharing — Share text content without creating files
- Folder upload — Upload entire folders with preserved structure
- Real-time progress — Progress bar + speed indicator (MB/s)
- Chunked upload — Large files up to 5GB with R2 multipart, 6 concurrent workers
- Upload resume — Resume interrupted chunked uploads automatically
- Folder ZIP download — Download all files in a folder as a streaming ZIP
- Password protection — Per-file download password
- Download limits — Set max downloads (1/5/10/50/100)
- File expiry — Flexible expiry (1h / 24h / 7d / 30d / Permanent)
- Blocked extensions — Configurable blocked file types (exe, bat, etc.)
- Rate limiting — IP-based download rate limiting (configurable window + max)
- API Key auth — Key-based upload permission control
- Key request & approval — Users apply online, admin approves
- SHA-256 hashing — Passwords never stored in plaintext
- Timing-safe comparison — Admin auth prevents timing attacks
- Short links — 6-char share codes
/s/AbC123 - Pickup codes — 6-digit numeric codes, like a locker
- QR codes — Auto-generated on share pages
- Media preview — Image / text / video / audio inline preview
- File type icons — Context-aware icons per file type
- Folder shares — Share a group of files as a folder with
/f/:id
- Dashboard — File count, storage, active keys, total downloads
- File management — Search, sort, filter by tag, view details, delete
- File tags — Add/remove tags on individual files
- Download history — View per-file download logs (IP, user agent, time)
- Batch operations — Bulk delete, set password, set expiry, add tags
- API Key management — Create, revoke, reactivate keys
- Request approval — One-click approve/reject key requests
- Password protected — Admin panel requires authentication
- Cloudflare Workers — Edge computing, zero cold start
- Cloudflare R2 — S3-compatible object storage with multipart upload
- Cloudflare KV — Low-latency key-value store with auto TTL
- Hono framework — Ultra-lightweight TypeScript web framework
- Config API — Frontend fetches server config (max size, blocked types) dynamically
- Inline frontend — All HTML/CSS/JS inlined in Worker
- CN/EN bilingual — One-click language toggle with localStorage persistence
- Light + Dark mode — System-aware theme with manual toggle
- Fully responsive — Perfect on desktop and mobile
- Cron cleanup — Automated expired file + folder cleanup
| Requirement | Description |
|---|---|
| Node.js 18+ | Runtime |
| Cloudflare account | Free tier is enough |
| R2 storage enabled | Needs to be activated in dashboard |
git clone <your-repo-url>
cd filefast
npm installFor beginners: If you don't have Node.js, download it from nodejs.org and install the LTS version. After installation, open a terminal and verify with
node -v.
npx wrangler loginThis opens a browser window. Click "Allow" to authorize Wrangler CLI.
First time using Wrangler? Wrangler is Cloudflare's official CLI tool, it's installed automatically with
npm install. No separate installation needed.
npx wrangler kv namespace create filefast-metaYou'll see output like:
🌀 Creating namespace with title "filefast-filefast-meta"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "KV", id = "abcd1234567890abcd1234567890abcd" }
Copy the id value — you'll need it in the next step.
npx wrangler r2 bucket create filefast-filesNote: If you haven't enabled R2 yet, go to Cloudflare Dashboard → R2 Object Storage → Click "Get Started" to enable it (free plan includes 10GB).
Open wrangler.toml and replace the KV namespace ID with the one from Step 3:
[[kv_namespaces]]
binding = "KV"
id = "abcd1234567890abcd1234567890abcd" # <-- Paste your ID hereChange the admin password (strongly recommended):
[vars]
ADMIN_PASSWORD = "YourStrongPassword123!" # <-- Change this!Optional settings:
# Max file size in bytes (default 500MB)
MAX_FILE_SIZE = "524288000"
# Allow upload without API Key (default false)
ALLOW_PUBLIC_UPLOAD = "false"Tip: Set
ALLOW_PUBLIC_UPLOAD = "true"if you want anyone to upload without an API Key. Otherwise users need to request a key from the admin panel.
npm run dev
# Open http://localhost:8787This starts a local development server for testing. All data is stored locally in .wrangler/state/.
npm run deployAfter successful deployment, you'll see:
✨ Successfully published your script to
https://filefast.your-subdomain.workers.dev
That's it! Your FileFast is live!
- Open your Worker URL → Click "Admin" in the top navigation
- Login with the password you set in
wrangler.toml - Create API Keys for your users, or set
ALLOW_PUBLIC_UPLOAD = "true"
Want to use your own domain (e.g. files.example.com) instead of *.workers.dev?
- Go to Cloudflare Dashboard → Workers & Pages
- Click your
filefastworker → Settings → Domains & Routes - Click "Add" → "Custom Domain"
- Enter your domain (e.g.
files.example.com) - Cloudflare will auto-configure DNS, wait for SSL cert (usually < 1 min)
| Variable | Default | Description |
|---|---|---|
ADMIN_PASSWORD |
FileFast@2024 |
Admin panel password |
MAX_FILE_SIZE |
5368709120 |
Max file size in bytes (5GB) |
ALLOW_PUBLIC_UPLOAD |
false |
Allow upload without API Key |
BLOCKED_EXTENSIONS |
exe,bat,cmd,scr,ps1,vbs |
Blocked file extensions (comma-separated) |
RATE_LIMIT_MAX |
20 |
Max downloads per IP per window |
RATE_LIMIT_WINDOW |
600 |
Rate limit window in seconds (10 min) |
| Method | Path | Description |
|---|---|---|
GET |
/ |
Home page |
GET |
/s/:code |
File share page |
GET |
/f/:groupId |
Folder share page |
GET |
/t/:code |
Text share page |
GET |
/api/config |
Server config (max size, blocked types) |
GET |
/api/info/:code |
File metadata |
POST |
/api/download/:code |
Download file (rate limited) |
POST |
/api/text |
Create text share |
POST |
/api/text/:code |
Get text content |
POST |
/api/pickup |
Pickup by code |
POST |
/api/request-key |
Request API Key |
| Method | Path | Description |
|---|---|---|
POST |
/api/upload |
Simple upload (< 10MB) |
POST |
/api/upload/init |
Chunked upload init |
POST |
/api/upload/part |
Upload a chunk |
POST |
/api/upload/complete |
Complete chunked upload |
GET |
/api/upload/status/:id |
Upload status |
DELETE |
/api/upload/abort |
Abort upload |
| Method | Path | Description |
|---|---|---|
POST |
/api/folder |
Create folder group |
GET |
/api/folder/:groupId |
Get folder info + files |
POST |
/api/folder/:groupId/zip |
Download folder as ZIP |
| Method | Path | Description |
|---|---|---|
GET |
/admin |
Admin panel |
POST |
/admin/login |
Admin login |
GET |
/admin/api/stats |
System stats |
GET |
/admin/api/files |
File list (supports ?q=, ?tag=, ?sort=, ?order=) |
DELETE |
/admin/api/file/:id |
Delete file |
POST |
/admin/api/file/:id/tags |
Update file tags |
GET |
/admin/api/file/:id/downloads |
Download history |
POST |
/admin/api/batch/delete |
Batch delete files |
POST |
/admin/api/batch/password |
Batch set password |
POST |
/admin/api/batch/expiry |
Batch set expiry |
POST |
/admin/api/batch/tags |
Batch add tags |
GET |
/admin/api/keys |
API Key list |
POST |
/admin/api/keys |
Create Key |
PATCH |
/admin/api/keys/:id |
Update Key status |
POST |
/admin/api/keys/:id/approve |
Approve Key request |
DELETE |
/admin/api/keys/:id |
Delete Key |
# Simple upload with cURL
curl -X POST https://your-worker.workers.dev/api/upload \
-H "X-API-Key: ff_your_api_key_here" \
-F "file=@./document.pdf" \
-F "password=optional_password" \
-F "expiry=86400" \
-F "maxDownloads=10"
# Response
{
"id": "AbCdEf123456",
"shareCode": "Xy7Km2",
"pickupCode": "482917",
"filename": "document.pdf",
"size": 1048576
}curl -X POST https://your-worker.workers.dev/api/text \
-H "Content-Type: application/json" \
-d '{
"content": "Hello, this is shared text!",
"expiry": 3600,
"password": ""
}'
# Response
{
"shareCode": "Mn3Kp8",
"pickupCode": "195283"
}curl -X POST https://your-worker.workers.dev/api/pickup \
-H "Content-Type: application/json" \
-d '{"code": "482917"}'
# Response
{
"type": "file",
"shareCode": "Xy7Km2",
"url": "/s/Xy7Km2"
}filefast/
├── src/
│ ├── index.ts # Routes + API handlers
│ ├── pages.ts # HTML page templates
│ ├── styles.ts # CSS theme + frontend utilities
│ ├── i18n.ts # CN/EN language dictionary
│ ├── chunked-upload.ts # R2 chunked upload API
│ └── types.ts # TypeScript type definitions
├── wrangler.toml # Cloudflare Workers config
├── package.json # Dependencies
├── tsconfig.json # TypeScript config
└── README.md # This file
| Key Pattern | Value | Purpose |
|---|---|---|
file:{id} |
FileMeta JSON |
File metadata (with tags, folder info) |
share:{code} |
fileId or folder:{id} |
Share code → File/Folder mapping |
pickup:{code} |
{type,id} JSON |
Pickup code index |
folder:{id} |
FolderGroup JSON |
Folder group data |
apikey:{id} |
ApiKeyData JSON |
API Key data |
keyindex:{hash} |
keyId |
Key hash → Key ID mapping |
text:{id} |
TextMeta JSON |
Text share data |
chunked:{id} |
ChunkedUploadMeta JSON |
In-progress chunked upload |
dllog:{fileId}:{ts} |
DownloadLog JSON |
Download history log (7-day TTL) |
ratelimit:{ip} |
{count,windowStart} |
Rate limit counter per IP |
filefast-files/
└── files/
└── {fileId}/
└── {filename} # Original file
- Password hashing — SHA-256, never plaintext
- Timing-safe comparison —
crypto.subtle.timingSafeEqualfor admin auth - XSS protection — All user input sanitized via
escapeHtml() - API Key hashing — Keys stored as SHA-256 hash, raw key shown once
- CORS headers — API endpoints configured with CORS
- Input validation — File size limits, required field checks
- Data isolation —
passwordHashnever exposed in admin API responses
Based on Cloudflare free / pay-as-you-go pricing:
| Resource | Free Tier | Overage |
|---|---|---|
| Workers requests | 100K/day | $0.50/million |
| KV reads | 100K/day | $0.50/million |
| KV writes | 1K/day | $5.00/million |
| R2 storage | 10 GB | $0.015/GB/month |
| R2 reads | 1M/month | $0.36/million |
| R2 writes | 1M/month | $4.50/million |
For small teams, daily usage typically stays within the free tier — nearly zero cost.
Make sure you ran npx wrangler r2 bucket create filefast-files and the bucket name in wrangler.toml matches exactly.
Verify you replaced YOUR_KV_NAMESPACE_ID in wrangler.toml with the actual ID from npx wrangler kv namespace create.
Either set ALLOW_PUBLIC_UPLOAD = "true" in wrangler.toml, or login to Admin panel and create an API Key first.
Run npx wrangler login again. Your token may have expired.
Just run npm run deploy again. Cloudflare Workers deploys are instant.
MIT