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.

1. Your Domain

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.

Registrar security

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.

How to fix it

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.

Dangling DNS

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.

How to fix it

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.

Email authentication

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.

How to fix it

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.

2. Your Accounts & Secrets

The fastest way into most apps isn't through the code — it's through a leaked API key or a compromised provider account.

MFA on everything that matters

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.

How to fix it

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.

  • Vercel: Settings → Security → Two-Factor Authentication
  • Supabase: Account → Security
  • GitHub: Settings → Password and authentication → Two-factor authentication
  • Stripe: Account settings → Team and security → Two-step authentication

Secrets in the right place

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.

How to fix 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.

Least-privilege API keys

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.

How to fix it

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:

  • Supabase: use the anon key for client-side, service_role only server-side when you need admin access
  • Stripe: create restricted keys in Dashboard → Developers → Restricted keys
  • GitHub: create fine-grained personal access tokens scoped to specific repos and permissions

Webhook signature validation

Check: 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.

How to fix it

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.

3. Your Third-Party Services

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.

Know what you've connected

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.

How to fix it

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.

Provider breach notifications

Check: You'll find out about security incidents at your providers before your users do.

How to fix it

Subscribe to the status page for every provider whose breach would affect your users. Takes five minutes total.

  • Vercel: https://www.vercel-status.com
  • Supabase: https://status.supabase.com
  • GitHub: https://www.githubstatus.com
  • Stripe: https://status.stripe.com
  • Cloudflare: https://www.cloudflarestatus.com

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.

4. Your Infrastructure

If you're on Vercel, most infrastructure security is handled for you. These are still worth a quick check.

HTTPS everywhere

Check: Your site runs on HTTPS with no mixed-content warnings.

How to fix it

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://.

Database access

Check: Your Supabase database isn't directly publicly accessible.

How to fix it

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.

Backups

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.

How to fix it

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:

  1. Create a new free Supabase project as a test environment
  2. In your production project: Settings → Backups → choose a backup → Restore → target the test project
  3. Verify: check that your most important tables have the right data
  4. Delete the test project when done

Document when you did this and what you verified. A restore you've tested is a restore you can trust.

5. Your Response Plan

You can't respond to something you don't know about. And you respond badly to something you haven't planned for.

Kill switch

Check: You can take your app offline in under five minutes if you need to.

How to fix it

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.

Incident plan

Check: You have a written answer to: "a secret was just compromised — what do I do?"

How to fix it — fill in this template
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.

Provider security contacts

Check: You know the security contact at your key providers.

The contacts
ProviderSecurity contact
Vercelsecurity@vercel.com
Supabasesecurity@supabase.io
GitHubhttps://github.com/security
Stripesecurity@stripe.com
Cloudflarehttps://www.cloudflare.com/abuse/
Netlifysecurity@netlify.com
Railwaysecurity@railway.app
Rendersecurity@render.com
Fly.iosecurity@fly.io
Resendsecurity@resend.com
SendGridsecurity@sendgrid.com
Twiliohttps://www.twilio.com/security
MongoDB Atlassecurity@mongodb.com
AWShttps://aws.amazon.com/security/vulnerability-reporting/

6. Your Team

If you're solo right now, bookmark this section for the moment you add a collaborator.

No shared accounts

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.

How to fix it

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.

Offboarding

Check: When someone leaves, you have a checklist of every place to revoke their access.

The checklist — run this at every departure
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: _________________________