I've shipped six products. Every single one had at least one launch-day issue I could have caught beforehand.
After the third time scrambling to fix something within hours of going live, I started keeping a checklist. Not a generic "best practices" article — the actual things I now verify before every deploy, in order.
This list comes from real configs. I'll show you the code.
Security (5 checks)
These come first because they're the hardest to fix after launch. Once your site is live and indexed, a missing security header or exposed endpoint is public.
1. Security Headers
Every response from your server should include these headers. Here's what I set in next.config.js for b4uship:
- Content-Security-Policy — Controls what resources the browser can load. This single header prevents most XSS attacks.
- Strict-Transport-Security — Forces HTTPS for 2 years. Include
preloadso browsers enforce it even on first visit. - X-Frame-Options: DENY — Prevents your site from being embedded in iframes (clickjacking).
- X-Content-Type-Options: nosniff — Stops browsers from guessing content types.
- Referrer-Policy — Controls what URL info is sent to other sites.
- Permissions-Policy — Disables camera, microphone, geolocation unless you need them.
If you're building a security scanner and forget your own security headers, that's not a good look. I caught this during my own dogfooding.
2. CORS Configuration
Check that Access-Control-Allow-Origin is NOT set to *. This is the default that AI-generated code almost always creates. Lock it down to your actual domain.
For b4uship, the backend on Modal.com only accepts requests from https://b4uship.com. During development, I had it set to * because "I'll fix it later." I almost shipped it that way.
3. Environment Variables
Go through every single environment variable your app uses. Verify:
- No secrets are prefixed with
NEXT_PUBLIC_(this exposes them to the client) - All required vars have values in production
- No default/fallback values that could mask missing configs
.envis in.gitignore
I use ! (TypeScript non-null assertion) on critical env vars so the app crashes immediately on startup instead of failing silently later:
```
const sql = neon(process.env.DATABASE_URL!);
```
4. Auth Scope
If you're using OAuth (GitHub, Google, etc.), check the permissions you're requesting. Request the minimum scope needed.
b4uship asks for read:user user:email repo. The repo scope is necessary because we need to clone private repos for code scanning. If your app doesn't need repo access, don't ask for it. Users notice, and they'll bounce.
5. Error Response Leakage
Search your codebase for catch blocks that return error details to the client. In production, users should see "Something went wrong." not a stack trace with your file structure.
This is the #1 thing AI-generated code gets wrong. Claude and Cursor both default to returning error.message or full error objects in API responses. Every single one of my projects had this issue before I started checking.
Infrastructure (4 checks)
6. Timeouts and Resource Limits
Every external call needs a timeout. Every serverless function needs resource limits.
For b4uship's scan engine on Modal.com:
- Code scan: 1.0 CPU, 512MB memory, 120-second timeout
- URL scan: 0.5 CPU, 256MB memory, 30-second timeout
- Retries: 0 (fail fast, don't retry expensive operations)
Without explicit limits, one user uploading a massive repo could eat your entire compute budget.
7. Health Check Endpoint
Add a /health or /api/health endpoint that returns { status: "healthy" }. This isn't optional — it's how monitoring tools (Vercel, UptimeRobot, your own scripts) verify your service is alive.
Takes 30 seconds to add. Saves hours of debugging "is it down or is it just me?"
8. Database Connection
Verify your database is accessible from production, not just from your local machine. If you're using Neon, PlanetScale, or Supabase, check that:
- Connection string points to production, not development
- SSL is enabled
- Connection pooling is configured for serverless
9. DNS and SSL
If you're using a custom domain:
- DNS is propagated (use
digto check) - SSL certificate is valid and auto-renewing
- HTTP redirects to HTTPS
- www redirects to non-www (or vice versa — pick one)
Payments (3 checks)
Skip this section if you're launching free. But if there's a payment button anywhere on your site, these are non-negotiable.
10. Stripe Keys
You have two sets of keys: test and live. Triple-check that production uses live keys. I've seen launches where the "Buy" button worked perfectly — in test mode. No real charges. The founder didn't notice for two days.
Also verify:
- Webhook signing secret is set
- Price IDs match your actual products
- Success and cancel redirect URLs point to production, not localhost
11. Payment Flow End-to-End
Actually buy your own product. Use a real card (or Stripe test card in test mode). Verify:
- Checkout page loads
- Payment completes
- Success redirect works
- The user gets what they paid for (content unlocked, feature enabled, email sent)
- Webhook fires and your database updates
If you skip this step and your first real customer can't complete checkout, you've lost them forever.
12. Graceful Payment Failures
What happens when payment fails? When the webhook doesn't fire? When Stripe is down?
b4uship handles this by making auth optional during checkout — if the auth system fails, checkout still works. The scan result still shows. The payment still processes. Every component is designed to degrade gracefully instead of blocking the core flow.
Launch Readiness (3 checks)
13. Remove Development Artifacts
Search your entire codebase for:
localhostURLs (including in API calls, redirects, and configs)console.logstatements with sensitive data- TODO comments that indicate unfinished features
- Disabled security checks ("temporarily" commented out)
- Test data in the database
14. Analytics
You need to know if anyone is actually using your product. At minimum:
- Google Analytics or Plausible for page views
- Error tracking (Sentry or similar)
- Basic event tracking for key actions (signup, scan, payment)
Without analytics, you're flying blind. You won't know if your launch worked, where users drop off, or what's broken.
15. Dogfooding
Use your own product as a real user would. Not a quick test — actually go through the full flow.
For b4uship, this meant running the scanner on b4uship's own code. The results found real issues: security headers I'd missed, error messages leaking internal paths. I fixed them before launching.
If your product can't pass its own quality bar, it's not ready.
The Meta-Lesson
After six products, I've learned that the launch itself is the least important part. The boring checks — headers, env vars, timeouts, payment flows — are what determine whether your first users have a good experience or hit a wall.
This checklist takes about 45 minutes to run through. It's saved me from launch-day disasters at least four times.
Copy it. Modify it for your stack. Run it every time.
ContentsTailor is a micro venture studio. We build products with partners, launch them together, and document every step — including the checklists. See all projects or apply to build with us.
