SaaS Security Checklist Before Launch: The MVP Guide
Shipping your MVP this week? Run this pragmatic, prioritized security pass first — covering auth, multi-tenancy, secrets, payments, and the few headers that actually matter.
Ship fast, but not blind
You don't need SOC 2 to launch an MVP. You do need to not leak your users' data on day one. This checklist is ruthlessly prioritized: the things that, left undone, end in an incident — not a nice-to-have wishlist.
Tier 1: Do not launch without these
Multi-tenant isolation
The #1 SaaS-specific bug: User A can read User B's data by changing an ID. Every query that touches tenant data must be scoped to the current tenant/user.
// WRONG — trusts the client's ID
const project = await db.project.findUnique({ where: { id } });
// RIGHT — scoped to the owner
const project = await db.project.findFirst({
where: { id, orgId: session.orgId },
});
If you use a database with row-level security (Postgres/Supabase), enforce it there too — defense in depth.
Secrets out of the codebase
grep -rniE "(sk_live|api[_-]?key|secret|password)\s*[:=]" src/
Anything not reading from process.env is a finding. And make sure your client bundle doesn't contain server secrets — only NEXT_PUBLIC_* (or your framework's public prefix) should ever reach the browser.
Real authentication
- Use a vetted provider/library (Auth.js, Clerk, Supabase Auth, WorkOS) — don't hand-roll password hashing.
- Hash with
bcrypt/argon2, never plain or MD5/SHA-1. - Rate-limit login, signup, and password reset.
- Send no "user exists" oracle on login or reset.
Input validation at every boundary
import { z } from "zod";
const Signup = z.object({
email: z.string().email(),
password: z.string().min(12).max(200),
});
const parsed = Signup.safeParse(body);
if (!parsed.success) return badRequest();
Tier 2: Do these before you have real users
Payment security
- Never touch raw card numbers — let Stripe/Paddle handle PCI scope.
- Verify webhook signatures. An unverified webhook lets anyone mark invoices paid:
const event = stripe.webhooks.constructEvent(rawBody, sig, endpointSecret);
- Use the raw request body for signature verification, not a parsed object.
Security headers
Set these once at the edge/middleware:
Strict-Transport-Security: max-age=63072000; includeSubDomains
Content-Security-Policy: default-src 'self'
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: DENY
Cookies and sessions
HttpOnly; Secure; SameSite=Lax on every auth cookie. Expire idle sessions. Invalidate sessions on password change.
Authorization, not just authentication
Logged-in ≠ allowed. Check roles/ownership on every privileged action, not just on page load.
Tier 3: Before you scale / take money seriously
- Rate limiting on all public and expensive endpoints.
- Logging and alerting — you can't respond to what you can't see. Log auth events and admin actions.
- Backups that you've actually tested restoring.
- Dependency hygiene:
npm auditin CI; patch high/critical. - A way to rotate secrets without a redeploy scramble.
- An email like
security@yourdomainso researchers can reach you.
The 10-minute pre-launch sweep
curl -sI https://yourapp.com— headers present?- Try to read another tenant's record by ID — does it 403?
grepfor secrets — clean?- Inspect the JS bundle — no server keys?
- Hit your Stripe webhook with a bad signature — rejected?
npm audit --omit=dev— no high/critical?- Cookies — HttpOnly + Secure + SameSite?
- Login endpoint — rate-limited?
What you can safely defer
Pentests, a bug-bounty program, SSO/SAML, and full compliance audits can wait until you have customers who ask. Don't let "we should get SOC 2" block a launch when the actual risk is an unscoped query.
Scan it with Troja
Troja runs the whole Tier 1 and Tier 2 sweep automatically — tenant isolation, exposed secrets, missing headers, weak cookies, unverified webhooks — and gives you a launch-readiness report with fix prompts. Scan before you flip the switch to public.
Run the scan this post is about.
Free, no signup. See what's hiding inside your walls in ~30 seconds.
Keep reading
All postsTroja vs. checkvibe: the closest scanner comparison (2026)
checkvibe pioneered security + SEO + AEO scanning with AI fix prompts and a 7-engine matrix. Troja matches it and adds connected deep-stack scans. The honest comparison.
ReadTroja vs. Fixnx: which AI website scanner should you use?
Fixnx runs 100+ AI-powered security, SEO and speed checks with credit-pack pricing. Troja adds AEO, connected deep-stack scans and per-finding AI fixes. Compared.
ReadTroja vs. CyScan.io: recon tool vs. fix-it scanner
CyScan.io is a free attack-surface recon scanner — endpoints, subdomains, fuzzing, screenshots. Troja is a fix-and-ship scanner with AI fixes, AEO and deep-stack scans.
Read