One-time file sharing. Drop a file, get a link, share it. The file deletes itself after the first download — or after 10 minutes, whichever happens first. No accounts, no cloud storage, no trace.
Live → flashdrop-bgdr.onrender.com
I needed to send files between my own devices without signing into Google Drive, WeTransfer, or whatever else wants my email. Most "secure file sharing" tools are either overkill (PGP, Magic Wormhole CLI) or not actually ephemeral (the file sits on someone's S3 bucket forever).
FlashDrop is the middle ground: run it locally or on a cheap VPS, share a link, done. The file is physically removed from disk after one download. There's a 10-minute TTL cleanup as a safety net.
You ──[upload]──> Server writes file to /uploads + stores metadata in memory
Returns a unique link: /d/<uuid>
Anyone ──[GET /d/<uuid>]──> Server streams the file back
Then immediately deletes it from disk
Link is now dead (404)
Meanwhile: a setInterval loop runs every 30s and purges
anything older than 10 minutes that wasn't downloaded
There's no database. Metadata lives in a JSON file so it survives server restarts, but that's it.
git clone https://github.com/Spacewalker215/FlashDrop.git
cd FlashDrop
npm install
node server.jsOpen http://localhost:3000. Drag a file in. You'll get a link.
Everything is hardcoded because it's a small tool, but the constants you'd want to change are at the top of server.js:
| Variable | Default | What it does |
|---|---|---|
PORT |
3000 (or process.env.PORT) |
Server port |
TTL_MS |
10 * 60 * 1000 |
How long before auto-delete (ms) |
CLEANUP_INTERVAL |
30 * 1000 |
How often the cleanup loop runs (ms) |
fileSize limit |
100 * 1024 * 1024 |
Max upload size (100 MB) |
If you want to change these, just edit the file directly. I didn't add a .env setup because it felt like overengineering for four variables.
.
├── server.js # Express server, upload/download routes, cleanup cron
├── public/
│ ├── index.html # Frontend (vanilla JS, no framework)
│ └── style.css # Dark UI, Inter font, minimal design
├── uploads/ # Temp storage — files live here briefly
├── package.json
└── .gitignore
POST /upload — Multipart form, field name file. Returns:
{
"success": true,
"id": "a1b2c3d4-...",
"link": "/d/a1b2c3d4-...",
"originalName": "document.pdf",
"size": 204800,
"expiresIn": "10 minutes"
}GET /d/:id — Downloads the file. The file is deleted from disk after the response completes. Hitting this endpoint again returns 404.
GET /info/:id — Check if a file still exists without downloading it. Returns { "exists": true/false } and remaining TTL.
FlashDrop runs helmet for security headers and express-rate-limit on every route:
| Route | Limit | Window |
|---|---|---|
| Global (all routes) | 100 requests | 1 minute |
POST /upload |
10 uploads | 15 minutes |
GET /d/:id |
30 downloads | 5 minutes |
Limits are per-IP. The server sets trust proxy so it works correctly behind reverse proxies (Render, Railway, Cloudflare, etc.).
This is a Node.js server — it needs a runtime, not static hosting.
Render (free tier):
- New → Web Service → connect this repo
- Build command:
npm install - Start command:
node server.js - Deploy
The port is read from process.env.PORT so it works out of the box on Render, Railway, Fly.io, or any platform that injects a port.
Note: On Render's free tier the filesystem is ephemeral (containers restart), which actually pairs well with FlashDrop's design — nothing is meant to persist anyway.
- Files are stored unencrypted on disk. If you need end-to-end encryption, this isn't it.
- Single-server only. No clustering, no shared storage.
- The 100 MB limit is arbitrary — bump it in
server.jsif you want, but keep in mind memory/disk on whatever you're hosting on.
MIT — do whatever you want with it.