Production Checklist
Six checks
An app is ready when all six are true:
- You see failures. Errors, logs, uptime, and an alert that wakes you.
- You can undo. Tested backups, safe migrations, rollback in one minute.
- Strangers stay out. Hardened auth, no secrets in the repo, rate limits, security headers.
- People find it. Domain, metadata, OG image, sitemap, search engines allowed.
- Money flows. Live keys, verified webhooks, spend caps on each metered service.
- The law is met. Privacy policy, terms, data deletion, a contact address a human reads.
Assumptions
Next.js on Vercel or Cloudflare. Postgres behind a pooler. Third-party email, payments, and error tracking.
The platform handles these. They are not on the list:
- TLS and the HTTP to HTTPS redirect
- DDoS mitigation (Vercel Firewall, Cloudflare WAF)
- CDN cache for static assets
- Rollback to a previous deployment
- Trailing-slash redirect (Next.js default)
Copy the list into docs/launch.md in each project. Delete lines that do not apply. Tick with a date.
Before the first user
Domain
- Apex is canonical.
wwwreturns 301 to apex. Vercel: markwwwas redirect. Cloudflare: one redirect rule - HSTS on. Vercel: default. Cloudflare: SSL/TLS → Edge Certificates
- Register the domain and host DNS on the app platform. Both are registrars. Transfer lock on. Auto-renew on. Expiry alert in calendar
- Custom 404 and 500 pages
-
robots.txtallows crawling. NoDisallow: /from preview - Previews are not indexable. Vercel: default
noindex, add Deployment Protection. Cloudflare Pages: header rule or Access policy -
sitemap.xmlreachable - Favicon,
apple-touch-icon,manifest.webmanifest -
<title>,<meta name="description">, canonical tag on each public route - OG image renders. Paste the URL into Slack or X to confirm
- Functions run in the database region. Vercel default is US East
-
Cache-Controlset on API routes that must not be cached
- SPF, DKIM, DMARC (
p=quarantineor stricter) on the sending domain - Transactional on
mail., marketing onnews.. Separate reputations - Each template checked on mobile and in dark mode
- Reply-to goes to an inbox a human reads
- One-click unsubscribe on all marketing email
Auth and security
- Callback URLs and allowed origins list production only. No
localhost - Sessions expire. Logout deletes the session on the server
- Password reset and OTP tested on the live domain
- Rate limits on login, OTP, signup, contact form, paid API calls. Set them in Vercel Firewall or Cloudflare WAF, not in app code
- Headers: CSP (report-only first),
X-Frame-Options,Referrer-Policy,Permissions-Policy - Secrets in the platform env store.
.env*in.gitignore. Git history scanned once - Env vars scoped per environment. Previews use a preview database and test payment keys
- Bot check on signup and contact forms: Cloudflare Turnstile or Vercel Bot Filter
- Each server action and API route checks the owner, not only the login1
- Uploads: size limit, type allowlist, separate origin
- Dependencies audited. Lockfile committed. No
latesttags
Data
- Daily backups on. Point-in-time recovery on if available
- One backup restored to a scratch database. One row read
- Migrations run in CI against a copy of the production schema
- Drop and rename done as expand, migrate, contract. Never one step
- Index on each foreign key.
EXPLAINon the three heaviest queries - Pooler URL in production, not the direct URL
- Database in UTC. Convert at the edge only
Observability
- Sentry, free tier. Client, server, edge. Source maps uploaded on deploy. Release = git SHA. One test error visible with a readable stack
- Sentry scrubbing on. No request bodies, cookies, or emails in events. Session replay off
- Structured logs with a request ID. One line per request minimum
- Log drain to external storage. Vercel keeps logs one hour (Hobby) or one day (Pro). Cloudflare Workers: Logpush
- Uptime check on the homepage and on a health route that reads the database
- Alerts go to your phone, not email
- Traffic analytics: Vercel Web Analytics or Cloudflare Web Analytics. Cookieless
- Product analytics: PostHog, free tier,
persistence: 'memory'. Server-side events where possible. Five events at launch: signup, activation, core action, upgrade, churn. Each one seen in the live dashboard - Analytics off on preview and localhost
- Web vitals on: Vercel Speed Insights or Cloudflare Web Analytics. LCP and CLS baseline recorded
Money
- Payment provider in live mode. Test keys removed from production env
- Webhook signatures verified. One live event replayed
- Webhook handlers idempotent. Same event twice grants once
- Spend cap or budget alert on each metered service: AI gateway, email, database, storage, maps
- Hosting spend capped. Vercel: Spend Management with hard pause. Cloudflare: billing alert plus a per-request limit in the Worker. No hard cap exists
- Failed payment and cancellation tested with a real card. Refunded
- Legal entity name and address on receipts
Legal
- Terms, privacy, refund, and cancellation policies forked from 37signals policies (CC BY 4.0, plain English, SaaS shape). Cookie policy and DMCA from Automattic Legalmattic (CC BY-SA) if needed. Keep the attribution line
-
docs/legal-facts.mdwritten before the fork is edited. The policies are rewritten from these facts, not from guesses. See the list below - Each policy read once against the product. Every sentence that describes a feature, vendor, data flow, or refund rule the product does not have is deleted or changed
- Policies live in
pages/legal/as Markdown. Effective date at the top. Changes go through git, so the history is the audit trail - Privacy policy and terms linked from footer and signup
- No cookie banner by default. Session cookie plus cookieless analytics need no consent under GDPR or ePrivacy. Say so in the privacy policy
- Before any marketing pixel or session replay: add c15t (
npx @c15t/cli). Open source, Next.js native, Google Consent Mode v2 built in. Hosted mode, free tier, so the backend detects the visitor’s jurisdiction. Pixel fires after opt-in only. Check the network tab withoverrides: { country: 'DE' } - Account deletion by the user. Deletes or anonymizes all data. Tested
- Contact address on the site. A human replies within one day
legal-facts.md
# Legal facts
## Entity
- Legal name, country, address
- Contact email for privacy requests
- Governing law and venue
## Product
- What the product does, one sentence
- Who can sign up (age minimum, business only, regions excluded)
- Free tier, trial length, what happens when it ends
## Money
- Billing period, currency, tax handling
- Refund rule (none, 14 days, prorated)
- Cancellation: who can cancel, when it takes effect, what happens to data
## Data collected
- Account: email, name, ...
- Usage: events, logs, IP, ...
- Content: files, messages, ...
- Payment: handled by <vendor>, card data never touches us
## Vendors that see user data
- Hosting, database, email, analytics, error tracking, AI, payments, support
- For each: vendor name, what it sees, region
## Retention
- Active accounts: kept while the account exists
- Deleted accounts: purged within N days
- Backups: N days
- Logs: N days
## AI
- Is user content sent to a model? Which vendor? Used for training? Opt-out?
## Cookies
- Session cookie only, or list the others and their purposeLaunch day
- Deploy from a git tag. Tag = release
- Incognito, on a phone: new account, core action, logout
- Sentry open for the first hour
- OG preview checked on the platform you post to
- Post. Then do not touch production for the day unless it is down
First week
- Read each error once. Fix or mute it
- Look at the 5 slowest routes and the 5 slowest queries
- Read the first support emails yourself
- Compare each bill to its cap
- Check backup timestamps. One per day
- Each monitor below shows data
- Add what you forgot to this list
Audits
All free. One tool per job. Run before launch and after any change to routes, metadata, headers, or email. Record scores in docs/launch.md.
- PageSpeed Insights, mobile, homepage plus two top routes. Performance 90 or more. Other three 100
- Is Agentic by Vercel, or
npx ax audit example.comin the terminal. Same engine (Ora). Agent readiness, 0 to 100. Fix each failing Essential check. Also catches missing sitemap, JSON-LD, and JS-only content - securityheaders.com. Grade A
- SSL Labs. Grade A+
- opengraph.xyz on each public route type. Then paste the URL into the real platform
- Rich Results Test on pages with JSON-LD. Zero errors
- mail-tester.com. One transactional, one marketing email. Score 10/10
- MXToolbox SPF and DMARC lookup on the sending domain
- Ahrefs Webmaster Tools site audit. Free for verified sites. Broken links, redirect chains, missing alt, duplicate titles, orphan pages
- WAVE on homepage and signup. Zero errors
- dnschecker.org after each DNS change. Apex,
www, mail subdomains - Cache review on homepage, pricing, logged-in home, one detail page, one API route. Read the response headers. Public routes:
HITon the second request. Routes behind auth:privateorno-store, neverHIT
Cache headers in one command
for u in / /pricing /app /api/me; do
echo "== $u"
curl -sI "https://example.com$u" \
| grep -i -E '^(cache-control|age|x-vercel-cache|cf-cache-status|vary|set-cookie):'
doneRun twice. Vercel: x-vercel-cache: HIT|MISS|STALE|BYPASS. Cloudflare: cf-cache-status: HIT|MISS|DYNAMIC|BYPASS. Run again with -H 'Cookie: ...'. Auth routes must show BYPASS or DYNAMIC.
Monitors
Set up once, before launch. Each needs domain verification. Do them in one sitting.
- Google Search Console. Domain property. Sitemap submitted. Email alerts on. Check the Pages report in week one
- Bing Webmaster Tools. Import from Search Console. Bing feeds DuckDuckGo, Yahoo, and ChatGPT search
- IndexNow. Cloudflare: Crawler Hints toggle. Vercel: ping the endpoint when the sitemap changes
- Ahrefs Webmaster Tools weekly crawl. Email summary on
- Uptime: UptimeRobot or Better Stack free tier. Homepage and health route. Five-minute interval. Phone alert
- Sentry alert on new issue types and error-rate spikes. Not on each event. Weekly digest on
- Analytics: Vercel or Cloudflare Web Analytics for traffic. PostHog for the five events. One saved insight per event
- Real-user Core Web Vitals: Search Console CWV report or Vercel Speed Insights. Google ranks on this, not on Lighthouse
- Google Alert on the product name and domain
- Billing thresholds on each metered service. Alert at 50% of the expected bill. Hard cap or pause at 200%. Write the expected number down
- Usage dashboards in one bookmark folder: bandwidth, function invocations, database compute and storage, AI tokens, emails sent. Open it each Monday for two months. Usage that grows faster than signups is a bug or a bot
Turn checks into gates
Each item you automate leaves the list. Automate these:
- Headers, redirects,
robots.txt, metadata: smoke test against the preview URL in CI - Secrets in the repo: pre-commit secret scanner
- Migration drift: pre-push check. Schema changed with no migration fails
- Backups: monthly job restores to a scratch database and counts rows
- Missing FK indexes: schema lint in CI
- Auth: integration test of the OTP flow against the preview deployment
- Lighthouse CI and
npx ax auditin the deploy pipeline. Fail on regression from the baseline
Keep these manual:
- The first backup restore
- The first errors and support emails
- The OG preview on the real platform
- The incognito phone walk-through
These show what the list is missing. When production breaks on something a line here would have caught, add the line the same day.
Footnotes
-
Insecure direct object reference. The query filters by
idbut not byid AND owner_id. Each list, detail, update, and delete needs the owner in theWHERE. ↩