Reference
Access control
Every site has an accessMode. Pick one at publish time, or change it
later with PATCH.
The three modes
| Mode | Who can view | Use it for |
|---|---|---|
public | Anyone with the URL | Marketing, demos, anything you'd link from Twitter. The default. |
private | Only you (signed in to the dashboard) | Drafts, internal reports, agent scratch space. |
password | Anyone with the URL and the password | Client previews, gated launches. |
Setting the mode at publish time
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:
# 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:
Set-Cookie: df_access_<slug>=<jwt>; Path=/; HttpOnly; SameSite=Lax; Max-Age=86400The 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
accessModefrompasswordtopublicdoes 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.