Reference

Access control

Every site has an accessMode. Pick one at publish time, or change it later with PATCH.

The three modes

ModeWho can viewUse it for
publicAnyone with the URLMarketing, demos, anything you'd link from Twitter. The default.
privateOnly you (signed in to the dashboard)Drafts, internal reports, agent scratch space.
passwordAnyone with the URL and the passwordClient previews, gated launches.

Setting the mode at publish time

bash
curl -X POST https://dropfast.dev/api/v1/sites \
  -H "Authorization: Bearer df_sk_..." \
  -F "file=@./index.html" \
  -F "name=client-preview" \
  -F "accessMode=password" \
  -F "password=open-sesame"

If accessMode is password, the password field is required — the request returns 400 PASSWORD_REQUIRED otherwise.

Changing the mode later

PATCH /api/v1/sites/{slug} accepts JSON. You can change accessMode, the password, or both:

bash
# Take a public site private
curl -X PATCH https://dropfast.dev/api/v1/sites/my-slug \
  -H "Authorization: Bearer df_sk_..." \
  -H "content-type: application/json" \
  -d '{"accessMode":"private"}'
 
# Rotate the password
curl -X PATCH https://dropfast.dev/api/v1/sites/my-slug \
  -H "Authorization: Bearer df_sk_..." \
  -H "content-type: application/json" \
  -d '{"password":"new-password-here"}'

How password mode works

A visitor lands on /s/<slug>/ and sees the password gate (a minimal, inline-styled page — no JS, no React). They submit the password to POST /api/v1/sites/{slug}/verify-password. On success, the response sets an HttpOnly, SameSite=Lax cookie scoped to the slug:

http
Set-Cookie: df_access_<slug>=<jwt>; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400

The cookie lasts 24 hours. Subsequent requests to any path under the slug skip the gate until it expires.

Rate limiting

The verify-password endpoint is rate-limited at 5 attempts per minute per IP. Above the limit, the endpoint returns 429 RATE_LIMITED. The limiter is in-process — it resets after one minute.

Private sites

A private site returns 403 to anyone who isn't signed in as the owner, along with a small "This site is private — sign in as the owner" page. The slug's existence is visible (the response is 403, not 404), so private mode protects the contents but not the fact that a site at this slug exists.

This means private sites are appropriate for drafts you'd rather not leak, but they're not a secret-store. Don't put credentials in them, and don't rely on the slug itself being unguessable.

Caching

Public sites are cached at Vercel's edge for up to 24 hours, with a 7-day stale-while-revalidate window on top. Browsers re-check after 60 seconds, so a viewer who hard-reloads sees fresh content quickly, but a viewer who just clicks the link again may see the previous version of your site for up to a day. PUT /api/v1/sites/{slug} (re-upload) does not yet invalidate the edge cache — viewers can force a refresh with Cmd/Ctrl+Shift+R, or you can append a cache-busting query string to the link you share.

Private and password-protected sites bypass the CDN entirely (Cache-Control: private, no-store), so changes to those propagate immediately.

Gotchas

  • Changing accessMode from password to public does not revoke existing cookies. Anyone whose cookie hasn't expired keeps access until it does (or you delete the site).
  • A password-protected site's bytes are still stored unencrypted in S3. Treat the password as a friction step, not as encryption.
  • Private mode is enforced at request time by /s/<slug>/[...path]. If you're serving DropFast through your own reverse proxy, make sure it forwards the Clerk cookie.
  • Deleting a site automatically deactivates any aliases pointing at it. The alias rows stay in your account so you can repoint them; the /_/<alias> URL starts returning 404 until you do.
Edit this page on GitHub