← Blog/Checklist

The Indie Dev Security Checklist: Ship Fast Without Getting Hacked

Security doesn't have to slow you down. But ignoring it will. Here's a practical pre-launch checklist for indie developers and startup teams — every item is actionable, fast, and specific about what to check and how.

·10 min read

Indie devs get security wrong in a predictable way: not from ignorance, but from timing. Security is the thing you'll "handle later" — after you validate the idea, after you get your first users, after you hit $1K MRR. Then you forget. Then something breaks.

This checklist is designed for the reality of how indie devs work: fast, solo or tiny team, shipping constantly, no dedicated security team. Each item is specific — what exactly to check, how to check it, and why attackers care.

The good news: most of these take under 30 minutes total to verify. A single automated scan covers the majority of them in 60 seconds.

Before You Launch

✅ 1. No Secret Keys in Client-Side JavaScript

What to check: Open your deployed app in Chrome. Go to DevTools → Sources → look in your JavaScript bundle files. Search for patterns like sk_live_ (Stripe), sk- (OpenAI), eyJ (JWTs), or any key you recognize from your .env file.

Why attackers care: Exposed secret keys are instant account compromise. A real startup founder lost $50K in Stripe charges in 4 hours because their live key was in a public JavaScript bundle. Attackers scan GitHub, npm packages, and JavaScript bundles continuously using automated tools.

Fix: Secret keys go on the server only. Use server-side API routes to proxy calls. Never use NEXT_PUBLIC_ for anything secret. Only publishable/public keys go to the client.

How Scantient helps: We scan your JavaScript bundles on every run for 20+ known API key patterns. If a key is exposed, you know before launch.

✅ 2. Security Headers Are Present and Correct

What to check: Go to securityheaders.com and paste your URL. You want an A or B grade. Specifically look for: Content-Security-Policy, Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy.

Why attackers care: Missing CSP means successful XSS attacks can steal session cookies. Missing X-Frame-Options enables clickjacking (your login page embedded in an attacker's iframe). Missing HSTS means you can be downgraded to HTTP by a man-in-the-middle.

Fix (Next.js): Add to next.config.js:

async headers() {
  return [{
    source: '/(.*)',
    headers: [
      { key: 'X-Frame-Options', value: 'DENY' },
      { key: 'X-Content-Type-Options', value: 'nosniff' },
      { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
      { key: 'Strict-Transport-Security', value: 'max-age=31536000' },
    ],
  }];
}

✅ 3. CORS Is Locked to Specific Origins

What to check: Open DevTools → Network. Find a request to your API. Look at the response headers for Access-Control-Allow-Origin. If it says *, that's a problem. It should say your exact domain: https://yourdomain.com.

Why attackers care: Access-Control-Allow-Origin: * with credentials: include means any website in the world can make authenticated API requests using your users' cookies. This is how data exfiltration via third-party sites works.

Fix: Never use * in production CORS policies. Use an explicit allowlist of your actual domains.

✅ 4. No Exposed Debug Endpoints

What to check: Try these URLs on your production domain:/.env, /.git/HEAD, /api/admin, /phpinfo.php, /wp-admin, /actuator/health (Spring Boot), /debug. They should all return 404, not 200 or 403.

Why attackers care: Attackers run automated scripts that probe every new domain on the internet for these paths within hours of launch. A /.env returning 200 exposes your database connection strings, API keys, and service credentials. This is how most indie dev breaches happen — not clever hacking, just checking obvious paths.

Fix: Return 404 (not 403) for these paths. 403 tells attackers the path exists. 404 is ambiguous.

✅ 5. All API Routes Have Server-Side Auth Checks

What to check: Open your API route files. For every endpoint that returns user-specific data, check that the first thing it does is verify a session token. Not check a variable. Not call a client-side function. Actually verify a JWT/session against your auth provider.

Why attackers care: Frontend-only auth is the most common critical vulnerability in indie dev apps. You check if the user is logged in the React component, but the API route itself accepts any request. A developer with curl can bypass your entire UI and access your data.

Fix: In every API route that handles sensitive data: const session = await getServerSession(authOptions); if (!session) return 401;. No exceptions.

✅ 6. SSL Certificate Is Valid and Monitored

What to check: Go to SSL Labs and run a test on your domain. You want grade A. Also note the expiry date — it should be 30+ days away.

Why attackers care: An expired certificate takes your site offline with a browser security warning. 100% of users see it and most leave. For B2B, it can kill a deal. For e-commerce, it stops all purchases instantly.

Fix: Use Let's Encrypt with auto-renewal (most platforms handle this). Set a calendar reminder 30 days before expiry for any manually managed cert. Or use Scantient to alert you automatically.

Infrastructure & Deployment

✅ 7. No Secrets in Git History

What to check: Run git log -p | grep -E '(password|secret|api.?key|token)' -i | head -50 in your repo. Also check if your .env file was ever committed: git log --all --full-history -- .env.

Why attackers care: GitHub is continuously scanned by automated tools looking for secrets. Even if you delete the file, it stays in git history. Even private repos can leak if you ever change visibility or a collaborator's account is compromised.

Fix: Use git-filter-repo to remove secrets from history. Rotate any credentials that were ever in git. Add .env* to .gitignore immediately.

✅ 8. Environment Variables Are Set Correctly in Production

What to check: Compare your production environment variable list against your .env.example file. Every variable that's required should be set. None should be test/placeholder values in production (look for: test_, sk_test_, example, your_key_here).

Why attackers care: Apps running with test API keys often have weaker validation, rate limiting, or logging. Stripe test mode processes don't charge real cards — which looks like a payment went through but nothing was billed.

Fix: Audit your Vercel/Railway/Heroku environment variable dashboard before launch. Every sk_test_ becomes sk_live_.

✅ 9. Database Connections Are Not Public

What to check: If you're using Supabase, check that RLS (Row Level Security) is enabled on all tables. If you're using a managed Postgres, check that the connection string requires SSL (sslmode=require) and that the database is not exposed to 0.0.0.0/0.

Why attackers care: An internet-facing database with weak credentials is compromised within minutes. This is one of the most common ways indie dev apps get data exfiltrated — not through the web app, but directly through the database.

Fix: Database should accept connections only from your app servers (private networking). Always use SSL for database connections. Enable RLS in Supabase.

Ongoing Monitoring

✅ 10. Rate Limiting on Authentication Endpoints

What to check: Try logging in with the wrong password 10 times in a row. Does your app slow down, show a captcha, or block the IP? If not, you have no rate limiting.

Why attackers care: Without rate limiting, attackers can run credential stuffing attacks — trying millions of email/password combinations from breached databases. If any of your users reuse passwords (most do), their accounts can be taken over.

Fix: Use Upstash Redis + @upstash/ratelimit in Next.js API routes. Or use a managed auth provider (Auth0, Clerk) that includes rate limiting.

✅ 11. Dependencies Are Updated and Scanned for CVEs

What to check: Run npm audit or pnpm audit in your project directory. Look for high and critical vulnerabilities. Also run npm outdated to see what's behind.

Why attackers care: Known CVEs in popular packages get exploited at scale. The Log4Shell vulnerability affected millions of systems. Left-pad, event-stream, node-ipc — supply chain attacks through npm packages are real and frequent.

Fix: Enable Dependabot in GitHub (free). Run npm audit fix weekly. Prioritize critical and high severity fixes. For medium, fix before the next launch.

✅ 12. Run a Full External Security Scan Before Launch

What to check: Run a free Scantient scan on your production URL. This covers all of the above in one pass — exposed secrets, security headers, CORS, debug endpoints, SSL, and more. No signup, no SDK, just your URL.

Why attackers care: An external scan mimics exactly what an attacker does in the first 5 minutes of probing a new target. If anything obvious is exposed, you'll know before they do.

How Scantient helps: The free scan covers all the categories above. For ongoing monitoring (so you know immediately when something changes in production), the lifetime deal is $79 — continuous monitoring with alerts, no recurring fees.

The Pre-Launch Checklist (Quick Reference)

  • No secret keys in client-side JavaScript (NEXT_PUBLIC_)
  • Security headers present: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy
  • CORS locked to specific origins, never *
  • No debug endpoints returning 200 in production
  • Every API route verifies auth server-side
  • SSL certificate valid and monitored for expiry
  • No secrets in git history
  • Production env vars set (no test keys in production)
  • Database not publicly accessible, RLS enabled
  • Rate limiting on auth endpoints
  • npm audit run, no critical/high CVEs
  • External security scan completed before launch

If you want to read more about the specific mistakes that cause the worst breaches, check the 7 API security mistakes killing startups post — each mistake maps to one of these checklist items with real-world examples.

Run the automated version of this checklist in 60 seconds

Free scan covers items 1–6 and 12 automatically. No signup. Just paste your URL.