Self-hostable · Cloudflare Workers + KV · MIT

Upload a .zip, get a public URL for your static site.

public-preview is a small Cloudflare Worker you deploy to your own account. Push a static site as a zip through the API, share the link, and it is served from Workers KV — then auto-expires in 7 days. No database, no separate host.

Everything you need, nothing you don't

One Worker. No build step for the server, no static host to configure.

Upload by API

POST a zip to /api/upload with an API key and get back a ready-to-share URL.

Public preview URLs

Anyone can open /p/{id} — HTML, CSS, JS and assets all served with correct MIME types.

Auto-expires in 7 days

Every KV key is written with a TTL, so old previews delete themselves. No cleanup cron.

Optional password

Add needAuth: true to gate a preview behind HTTP Basic Auth. The page and all its assets are protected.

Smart zip handling

Strips __MACOSX/.DS_Store junk, normalizes slashes, and auto-flattens a wrapping dist/ folder.

Free-tier friendly

Stays within Cloudflare Workers + KV free limits. Run it at zero cost for personal use.

How it works

Three steps, all running on Cloudflare’s edge.

1. Upload

You POST a zip + your API key to /api/upload. The Worker unzips it in memory.

2. Store

Each file is written to Workers KV under file:{id}:path, plus a small manifest, each with a 7-day TTL.

3. Serve

GET /p/{id} reads from KV and streams the bytes back with the right Content-Type.

# you
curl -X POST https://<your-worker>/api/upload \
  -H "X-API-Key: <key>" -F "file=@site.zip"
{"ok":true,"url":"https://<your-worker>/p/a1b2c3d4e5f6g7h8/","expiresAt":"…"}

# anyone
curl https://<your-worker>/p/a1b2c3d4e5f6g7h8/   # → 200, the rendered site

Deploy your own in 5 minutes

Fork or clone the repo, then run a few commands. The deployment is yours — your account, your domain, your limits.

Clone the repository

Fork it on GitHub or clone directly:

git clone https://github.com/zxdong262-apps/public-preview.git

 
cd public-preview && npm install

Log in to Cloudflare

Authorize Wrangler with your Cloudflare account (one-time):

npx wrangler login

Create the KV namespace

This is where uploaded files live. Copy the returned id into wrangler.toml:

npx wrangler kv namespace create SITES

Set your API key

Generate a secret key and add it as a Worker secret (and to .dev.vars for local dev):

openssl rand -hex 24 > temp/key.txt
echo "API_KEY=$(cat temp/key.txt)" > .dev.vars
npx wrangler secret put API_KEY < temp/key.txt

Build the home page & deploy

Pug compiles dist/index.html, then Wrangler ships the Worker + static assets:

npm run deploy

You’ll get a URL like https://public-preview.<you>.workers.dev. Add a custom domain in the dashboard if you like.

This is not a hosted product. There is no sign-up, no shared backend, no usage shared with anyone else. You deploy the Worker to your own Cloudflare account, so you own the uploaded content, the API key, and the billing — which for personal use sits comfortably inside the free tier.

Using the API

Authenticate with your API key, POST a zip, get a URL back. Optionally password-protect it.

curl
JavaScript
Password-protected
# upload a zip (index.html + assets at the archive root)
curl -X POST https://<your-worker>/api/upload \
  -H "X-API-Key: $API_KEY" \
  -F "file=@site.zip"

# → 201 Created
{"ok":true,"id":"a1b2c3…","url":"https://<your-worker>/p/a1b2c3…/","expiresAt":"…","auth":false}
const fd = new FormData();
fd.append("file", fileInput.files[0]); // a .zip

const res = await fetch("/api/upload", {
  method: "POST",
  headers: { "X-API-Key": API_KEY },
  body: fd,
});
const { url, expiresAt } = await res.json();
# add the `needAuth` header; a password is generated and returned
curl -X POST https://<your-worker>/api/upload \
  -H "X-API-Key: $API_KEY" \
  -H "needAuth: true" \
  -F "file=@site.zip"

# → {"ok":true,"url":"…/p/…/","auth":true,"password":"vwxggzfxyrxd"}

# visiting the URL now prompts for the password (username: anything)
curl -u anything:vwxggzfxyrxd https://<your-worker>/p/…/