File Share
A personal file sharer and URL shortener. Upload anything or paste a long link, and get a short public URL and a QR code. It runs on one Cloudflare Worker with R2 and KV.





What it does
- Uploads any type of file, several at a time, by drag and drop or from buttons that open the camera, the audio picker or the file chooser on a phone.
- Gives each file a short random ID and a public share page. The page adapts to the file: an audio or video player, an inline image, an embedded PDF with a download link, or just a download link for anything else. Pasted links get the same kind of short ID and redirect straight to their destination.
- Optional expiry date for each file or link. After that day an expired page is shown, with a 410 status, instead of the content.
- The admin page lists everything with previews, upload dates, expiry dates and a click count, lets files be renamed and deleted in bulk, and shows each link as a QR code that can be copied as a link or an image.
- Installable as a progressive web app. It registers as a share target, so sharing a file or a link to it from another app on a phone uploads the file or shortens the link, then opens the admin page.
- Share links carry Open Graph and Twitter card tags, so they preview sensibly in chat apps.
Under the hood
- A single Worker in plain JavaScript with no framework. File bytes go in an R2 bucket and the metadata (name, type, size, dates, owner, clicks, or the destination URL for a link) goes in KV, both under the same ID.
- IDs are drawn from a 64-character alphabet using the Web Crypto random source: 22 characters for files and 11 for links. Links are shorter by design and are meant to be typed or read out; files use the longer ID so they cannot be guessed. The public routes are
/r/id,/idfor a bare ID, and/file/idfor the raw bytes. - The admin page and the whole API sit behind Cloudflare Access, and the Worker does not simply trust that. It checks the Access token on every gated request (signature, issuer, audience and expiry) and takes the user's identity from it. It fails closed, refusing everything until it is configured, and it refuses service tokens because every record is scoped to its owner.
- State-changing API calls that arrive with a cross-origin
Originheader are refused, since the Access cookie would otherwise ride along on cross-site requests. - Uploads are served from the same origin as the admin page, so uploaded HTML, SVG and XML are sent with a sandboxing content security policy to stop them running script. The Worker runs first for every request, including static assets, so every response gets a content security policy,
nosniffand a no-referrer policy. The admin page's policy allows scripts only from its own files, plus the one CDN that serves the QR code library; the pages the Worker renders for the public allow no script at all. - Raw files support HTTP range requests so audio and video can be scrubbed, and are served with long-lived immutable caching because an ID never changes meaning.
- Click counts are a read-then-write on KV, which has no atomic increment, so they are best-effort rather than exact. The count happens in the background so it does not slow the response.
- Deployment is by git push through Cloudflare's own build integration. Tests use Node's built-in test runner against the auth and security-header code.
The screenshots are from a local test instance with invented files.