Pre-launch checklist
Work through this before real users arrive. Each item is written so you can check it yourself in a few minutes. If an item doesn't apply to your app, strike it out rather than leaving it blank, so you know you looked.
When something fails, the matching prompt in 05-agent-prompts.md will help your coding agent fix it. Re-test it yourself afterwards. A fix you haven't checked is not a fix.
App: ______________________ Reviewed by: ______________ Date: ____________
---
1. Security
Keys and secrets
- No secret keys appear in client code. Open your live site, view source and search the loaded JavaScript for
sk_,secret,service_role,private,api_keyand your AI provider's key prefix. - Only values meant to be public use a public prefix (
NEXT_PUBLIC_,VITE_,PUBLIC_,EXPO_PUBLIC_,REACT_APP_). Everything with one of these prefixes ends up in the browser. .envand similar files are in.gitignore, and your git history has never contained them. If it has, rotate those keys (see04-secrets/secrets-guide.md).- Production and development use different keys, databases and payment accounts.
Database and storage
- Every table that the browser can reach has access rules turned on (row-level security in Supabase or Postgres, security rules in Firebase).
- You tested those rules as a logged-out visitor and as a second user. Neither can read or change the first user's data.
- Storage buckets are private unless a file is truly meant for anyone on the internet. Uploaded user files are not in a public bucket.
- Upload size and file type are limited on the server, not only in the form.
Server routes and auth
- Every API route, server action and edge function that touches user data checks who is calling and whether they're allowed to.
- IDs in URLs and request bodies are checked against the signed-in user. Changing
?id=41to?id=42doesn't show someone else's record. - Admin pages and routes are protected on the server. Hiding a button is not protection.
- Password reset, magic link and email change flows expire and can only be used once.
Abuse protection
- Login, sign-up, password reset, contact forms and any AI feature have rate limits.
- Paid third-party calls (AI models, SMS, email, maps) have a per-user cap and an account-wide spending limit set with the provider.
- Error messages shown to users are short and generic. Stack traces, SQL and file paths only go to your logs.
- CORS allows only your own domains, not
*, on any route that uses cookies or tokens. - Security headers are in place (
03-security-headers/).
AI features
- System instructions and API keys for AI features live on the server.
- The model can only call the tools and data it needs for that feature, and actions that change or delete data need a separate permission check.
- Model output is treated as untrusted text. It is not rendered as raw HTML or run as code or SQL.
- You tried at least five prompt injection attempts (see
02-security-hardening.md, section 9) and none of them leaked data or ran an action.
Dependencies
- You ran your package manager's audit (
npm audit,pnpm auditor similar) and fixed or consciously accepted each high or critical result. - There are no packages you don't recognise. Generated code sometimes imports a package that doesn't exist or has a near-identical name to a real one. Check each unfamiliar name on the registry.
- Your lockfile is committed.
2. Privacy
- You have written down what personal data you collect, why, where it's stored and who processes it (hosting, database, email, analytics, payments, AI provider).
- You collect only what the app needs. Optional fields are marked optional.
- Users can delete their account and data, or at least request it by email with a clear response time.
- Logs don't store passwords, full card numbers, tokens or the full content of private messages.
- If you send user content to an AI provider, your privacy policy says so, and you've checked whether that provider uses it for training and how long it keeps it.
- If you have users in the EU or UK, you've checked whether you need a cookie consent prompt. You only need one for non-essential cookies and similar storage.
3. Payments
- You use a hosted checkout or payment element from your provider, so card details never touch your server.
- Prices, plan IDs and amounts are decided on the server. The client sends a plan or product ID, never a price.
- Webhooks verify the provider's signature before doing anything.
- Webhook handlers are safe to run twice for the same event (providers retry).
- Access is granted from the webhook or a server-side check of the payment, not from the browser landing on a "success" page.
- Cancelling, failed renewals, refunds and disputes all remove or adjust access correctly.
- Live keys are only in production. You've made one real purchase with a real card and refunded it.
- Receipts, tax settings and your business details in the payment dashboard are correct.
4. Performance
- Lighthouse or PageSpeed Insights on your main pages, on mobile, shows no red scores you haven't looked into.
- Images are resized for display and served in a modern format (WebP or AVIF) with width and height set.
- Fonts are limited to the weights you use and load with
font-display: swap. - Database queries on list pages are paginated and the columns you filter by are indexed.
- Static assets have long cache headers. HTML is not cached for long.
- The app still works on a slow 3G profile in your browser's developer tools.
5. Accessibility
- You can use every main flow with only a keyboard, and the focus outline is visible.
- Every form field has a visible label, and errors say what went wrong and how to fix it.
- Text contrast meets WCAG 2.2 AA (4.5:1 for normal text, 3:1 for large text).
- Images that carry meaning have alt text. Decorative images have empty alt text.
- Buttons are real
<button>elements and links are real<a>elements. - The page has one
<h1>, headings are in order and the<html>element has alangattribute. - The site works when zoomed to 200% and on a 320 px wide screen.
- Motion respects
prefers-reduced-motion.
6. SEO and sharing
- Each public page has a unique
<title>and meta description. - Open Graph and social preview tags are set, and the preview image looks right when you paste the link into a chat app.
robots.txtdoesn't block your whole site (generated projects sometimes ship withDisallow: /).- Private and logged-in pages are
noindexor blocked. - You have a
sitemap.xmllisting public pages and you've submitted it to the search consoles you use. - Canonical URLs are set, and
wwwand non-wwwredirect to one version. - You have a favicon and an Apple touch icon.
7. Analytics without creepiness
- You know the five or so questions you want analytics to answer. Track those, not everything.
- You use a privacy-friendly analytics tool or self-hosted option where you can, and you've checked whether it sets cookies.
- Session recording is off, or masks all text inputs and is mentioned in your privacy policy.
- No personal data (emails, names, user IDs that map to people) is sent in event names, URLs or query strings.
- Your privacy policy names your analytics provider.
8. Backups
- Your database has automatic backups, and you know how far back they go.
- You have restored a backup into a separate database at least once and checked the data.
- User uploads are backed up or stored with versioning.
- Your code is in a git host, and someone other than you could deploy it from the README.
- Domain, DNS and hosting accounts have two-factor authentication and recovery codes stored safely.
9. Error monitoring and uptime
- Front-end and back-end errors go to an error tracker that alerts you.
- The error tracker scrubs personal data and secrets before sending.
- An uptime check pings your home page and one API route every few minutes and alerts you by email or phone.
- You get alerts for failed payment webhooks and failed scheduled jobs.
- Domain and SSL certificate expiry dates are in your calendar with reminders.
10. Legal pages
These are general pointers, not legal advice. Your obligations depend on where you and your users are.
- Privacy policy that matches what the app actually does, linked in the footer and at sign-up.
- Terms of service, linked in the footer and at sign-up.
- Refund and cancellation policy if you take money, linked near the buy button.
- Cookie notice or consent prompt only if you use non-essential cookies.
- A contact method that a real person reads.
- Business name and address where local law requires it (for example, an imprint page in Germany).
- If you use AI to generate content shown to users, you've checked whether any disclosure rules apply where you operate.
---
Sign-off
| Area | Status | Owner | Notes |
|---|---|---|---|
| Security | |||
| Privacy | |||
| Payments | |||
| Performance | |||
| Accessibility | |||
| SEO and sharing | |||
| Analytics | |||
| Backups | |||
| Monitoring | |||
| Legal pages |
Ready to launch: [ ] Yes [ ] Not yet