Your AI scanned your code. This covers everything else.
A code scan can only see what's in your source files. It can't see whether your domain registrar has a weak password, whether your Supabase backups have ever been tested, or whether a former collaborator still has access to your GitHub repo. These are the things that actually get products breached — not because they're complicated, but because no one told you to check.
Work through this the same way you worked through your code findings: one item at a time. Most take under 15 minutes. The How to fix it steps are there when you need them — collapse them when you don't.
Written for solo founders on the most common stack: Next.js, Supabase, Vercel, GitHub, Stripe. If your setup is different, the principles are the same.
Your domain is the front door to everything. If someone takes control of it, they can redirect your users, intercept your email, and impersonate your product. This is the highest-leverage 15 minutes you'll spend.
Check: Your domain registrar account has MFA enabled and transfer lock is on.
Your registrar — Cloudflare, Namecheap, GoDaddy, Squarespace Domains — controls your entire domain. A compromised registrar account lets an attacker point your domain at their own server. MFA prevents account takeover; transfer lock prevents the domain being moved without your approval.
Cloudflare (most common): Account → My Profile → Authentication → Two-Factor Authentication → Enable. Domains are transfer-locked by default. Verify: Domains → your domain → Configuration → Transfer lock = Locked.
Namecheap: Profile → Account → Security → Two-Factor Authentication → Enable. Transfer lock: Domain List → Manage → Domain → Transfer lock = On.
Squarespace Domains: Account → Security → 2-Step Verification. Transfer lock: Domains → your domain → Security → Transfer lock = Locked.
GoDaddy: Account Settings → Login & PIN → 2-Step Verification → Set Up. Domain lock: My Products → your domain → Settings → Domain lock = On.
Porkbun: Account → Account Settings → Two-Factor Auth. Transfer lock is on by default.
Use an authenticator app (1Password, Authy, Google Authenticator) rather than SMS. SMS is vulnerable to SIM-swap.
Check: Your DNS records don't point to services you've shut down.
When you decommission a Vercel preview or any other service, the DNS record often stays. Anyone who claims that hostname at the provider can serve content from your subdomain — a phishing page that looks exactly like yours.
Log into wherever you manage DNS (usually Cloudflare or your registrar). Go through every CNAME and A record. For each one, ask: is this service still active and do I still own it? Delete anything pointing somewhere you no longer own.
Check: If your domain sends email, SPF, DKIM, and DMARC records are configured.
Without these, anyone can send email that appears to come from your domain.
Your email provider (Resend, SendGrid, Postmark) has a setup guide for this — search "[your provider] SPF DKIM DMARC setup". Under 30 minutes. They walk you through adding the right DNS records.
The fastest way into most apps isn't through the code — it's through a leaked API key or a compromised provider account.
Check: Vercel, Supabase, GitHub, Stripe, and your domain registrar all have MFA enabled.
These five accounts, compromised, can take down your product or expose all your users' data. Password-only protection is not enough for any of them.
Go to each service's security settings and enable MFA with an authenticator app. Five minutes per service. Do it now while you're thinking about it.
Check: API keys and secrets live in environment variables — not in your code, not in Slack, not in a notes app.
Your .env file should never be committed to GitHub. If you've ever pasted an API key into a chat message, assume it's compromised and rotate it.
Check your GitHub repo for any committed .env files: search for .env in your repo's file list. If you find one, remove it, rotate every key in it, and add .env to your .gitignore.
For production secrets: use Vercel's environment variables dashboard (Project → Settings → Environment Variables). Never put real secrets in your code.
If you've shared a key in Slack or a chat: go to that provider's dashboard, find the key, and rotate it. A rotated key is harmless; a leaked key is not.
Check: Your API keys only have the permissions they actually need.
A Supabase service role key used where a read-only key would do is a much larger blast radius if it leaks.
Go through each key in your Vercel environment variables. For each one, ask: does this key have more access than the feature using it needs? Most providers let you create scoped keys:
anon key for client-side, service_role only server-side when you need admin accessCheck: Stripe webhooks (and any other incoming webhooks) validate the signature before processing.
A webhook endpoint that processes any incoming request without verifying it came from Stripe can be triggered by anyone who finds the URL.
Your code scan will flag this if it's missing. If it flagged it, fix it in your code first — paste the finding back into Claude Code and ask it to fix it.
If you want to verify manually: find your Stripe webhook handler and confirm it calls stripe.webhooks.constructEvent(payload, sig, secret) before doing anything with the event data.
Every service you've connected is part of your attack surface. A breach at Supabase, Stripe, or your email provider affects your users even if your code is perfect.
Check: You can list every third-party service that receives your users' data.
Your database, auth provider, payment processor, email service, analytics — each one is a potential breach vector. You don't need to avoid them. You need to know they're there.
Write the list. It takes five minutes. For each service, note: what user data does it receive, and what would happen if it was breached? That's your risk picture.
Check: You'll find out about security incidents at your providers before your users do.
Subscribe to the status page for every provider whose breach would affect your users. Takes five minutes total.
Also enable Dependabot in your GitHub repo settings — it will automatically flag vulnerable npm packages.
If you store user email addresses, register your domain at haveibeenpwned.com/DomainSearch. You'll be notified when your users' emails appear in a breach.
If you're on Vercel, most infrastructure security is handled for you. These are still worth a quick check.
Check: Your site runs on HTTPS with no mixed-content warnings.
Vercel enables HTTPS by default. Open your site in Chrome, open DevTools (F12) → Console. Any mixed-content warnings will appear there. Fix by ensuring all assets (images, scripts, fonts) load over https://, not http://.
Check: Your Supabase database isn't directly publicly accessible.
Supabase dashboard → Project Settings → Database → Connection string. The API and connection pooler are the right ways in. Check Network Restrictions under Project Settings → Network — make sure you haven't opened the database to 0.0.0.0/0 without a specific reason.
Check: Backups exist and you've tested a restore at least once.
Supabase Pro includes daily backups. Free tier does not. A backup you've never restored is an assumption, not a safety net.
Check if you have backups: Supabase dashboard → Project Settings → Backups. If you're on free tier and see nothing, you need to set up backups manually before launch — this is critical.
Test a restore:
Document when you did this and what you verified. A restore you've tested is a restore you can trust.
You can't respond to something you don't know about. And you respond badly to something you haven't planned for.
Check: You can take your app offline in under five minutes if you need to.
Know these steps before you need them. Write them somewhere accessible outside your app.
Take Vercel deployment offline:
Vercel dashboard → your project → Deployments → click the active deployment → Remove from production. Immediate. Restore by re-promoting any previous deployment.
Rotate a compromised Supabase key:
Supabase dashboard → Project Settings → API → rotate the service_role key. This immediately invalidates all connections using the old key.
Pause the Supabase database (nuclear option):
Project Settings → General → Pause project.
Write down the steps for your specific setup. The time to figure this out is not during an incident.
Check: You have a written answer to: "a secret was just compromised — what do I do?"
Last updated: ______________________
Who to call first: _________________ Contact: _______________
Step 1 — Write down what happened
What: _______________________________________________
When: _______________________________________________
How discovered: _____________________________________
Step 2 — Don't destroy evidence
Do NOT wipe logs or restart services until you've documented the state.
Step 3 — Contain
Take app offline? [ ] Yes [ ] No [ ] Not yet
Rotate compromised secrets (in this order):
1. _________________________________________________
2. _________________________________________________
3. _________________________________________________
Step 4 — Notify
Users affected? [ ] Yes [ ] No [ ] Unknown
Legal notification required? (GDPR: 72h if EU users)
Notify: ____________________________________________
Step 5 — Fix and restore
Root cause identified: ______________________________
Fix applied: _______________________________________
Verified before restoring: [ ] Yes
Step 6 — After
Write up what happened. Update this plan.
Check: You know the security contact at your key providers.
| Provider | Security contact |
|---|---|
| Vercel | security@vercel.com |
| Supabase | security@supabase.io |
| GitHub | https://github.com/security |
| Stripe | security@stripe.com |
| Cloudflare | https://www.cloudflare.com/abuse/ |
| Netlify | security@netlify.com |
| Railway | security@railway.app |
| Render | security@render.com |
| Fly.io | security@fly.io |
| Resend | security@resend.com |
| SendGrid | security@sendgrid.com |
| Twilio | https://www.twilio.com/security |
| MongoDB Atlas | security@mongodb.com |
| AWS | https://aws.amazon.com/security/vulnerability-reporting/ |
If you're solo right now, bookmark this section for the moment you add a collaborator.
Check: Everyone with production access has their own credentials — nothing shared.
Shared credentials can't be individually revoked. When someone leaves, you have to rotate everything they had access to — and this almost always gets skipped.
Go through each service and ask: is anyone sharing a login?
Common places this happens: a single GitHub account everyone uses, a shared Supabase project owner login, a Stripe account with one login for the whole team.
Fix: create individual accounts for each person. Most services are free to add team members. Where individual accounts aren't possible, use a team password manager (1Password Teams, Bitwarden Business) so you can revoke access when someone leaves.
Check: When someone leaves, you have a checklist of every place to revoke their access.
Person: ____________________________ Date: ______________________________ Completed by: ______________________ [ ] GitHub — removed from org/repo, personal access tokens revoked [ ] Vercel — team access removed [ ] Supabase — org access removed [ ] Stripe — dashboard access removed [ ] Domain registrar — access removed if they had it [ ] Netlify / Render / Railway / Fly.io — access removed [ ] Slack / Discord / team chat — removed [ ] Password manager — removed [ ] Any API keys they generated — reviewed and rotated if needed [ ] Any shared credentials they knew — rotated Completed: _________________________