Cursor App Security Checklist
Cursor will happily help you build almost anything, in any stack. That also means there's no template doing the security work for you. Here's what to check before strangers start clicking around, plus a rule file so the next AI edit doesn't quietly undo it.
Not affiliated with Cursor. Just big fans of people shipping things.
TL;DR
Secrets live on the server, never in public env vars. Every API route checks who is calling and whether they own the data. Validate input on the server. Rate-limit anything that costs money. Add security headers. Then write it all down in
.cursor/rulesso Cursor follows it next time.
1. Keep secrets out of the browser (and out of git)
Anything prefixed NEXT_PUBLIC_, VITE_ or REACT_APP_ is built into your JavaScript and downloaded by every visitor. That's fine for public values like a Supabase anon key or a Stripe pk_ key. AI keys, Stripe sk_ keys, database URLs and Supabase service_role keys belong in server-only env vars, read by API routes or edge functions.
How to check: Search your project for those prefixes and for hardcoded strings like sk-, sk_live_ and service_role. Then search your live JS in DevTools too. Make sure .env files are in .gitignore and run git log --all -- .env to see if one was ever committed. If it was, rotate every key in it.
More detail: how to hide API keys in Vite/React and how to find leaked keys in your bundle.
Prompt to paste into Cursor
Find every secret or API key used in this project. For each one, tell me whether it's exposed to the browser (hardcoded in client code, or in a NEXT_PUBLIC_/VITE_/REACT_APP_ variable). Move any secret into a server-only env var used by an API route or edge function, and make sure .env files are in .gitignore. List which keys I should rotate.
2. Tell Cursor which files to stay out of (.cursorignore)
Cursor already ignores files in your .gitignore plus a default list that includes .env*. A .cursorignore file in your project root (same syntax as .gitignore) adds anything else, like credential files that aren't gitignored. Ignored files are blocked from Agent, Tab, Inline Edit and @ mentions.
# .cursorignore **/credentials.json **/secrets.json **/*.pem **/*.key
Two honest caveats from Cursor's own docs: the terminal and MCP tools the Agent uses can't be blocked by .cursorignore, and complete protection isn't guaranteed. So treat it as a seatbelt, not a vault. Review terminal commands before you approve them, and keep real production secrets out of your laptop's project folder where you can.
Want a file kept out of the codebase index (and search results) but still usable by the AI when you explicitly reference it? That's what .cursorindexingignore is for. It's for noise, not secrets: for sensitive files use .cursorignore.
3. Check authentication on every route
AI-generated API routes often work perfectly… for anyone. Hiding a button in the UI doesn't protect the endpoint behind it. Every API route, server action or edge function that reads or changes private data should verify the session on the server before doing anything.
Quick test: Sign out, then call one of your API routes directly with curl or the browser. If you get data back, that route needs an auth check.
4. Check authorization (do they own it?)
Being signed in isn't the same as being allowed. A route like /api/invoices/123 must also confirm invoice 123 belongs to the caller. Otherwise anyone signed in can change the number and read someone else's data.
Quick test: Make two accounts. As user A, copy a request for your own data, change the ID to one of user B's, and send it. You should get a 403 or 404, not B's data. On Supabase, this is what Row Level Security is for: the Supabase RLS checklist.
Prompt to paste into Cursor
Go through every API route, server action and edge function. For each, tell me: does it verify the signed-in user on the server, and does it check that user owns the resource they're reading or changing? Fix any that don't, using the existing auth setup. Never trust a user ID sent from the client.
5. Validate input on the server
Client-side form validation is for nice UX. It does nothing against someone sending requests directly. Validate type, length and allowed values on the server for every endpoint, ideally with a schema library like zod. Use your database client's parameterized queries instead of building SQL strings, and don't render user input as raw HTML.
While you're there: return a generic error to the client (like "Something went wrong") and log the details on the server. Stack traces and upstream API errors can leak more than you'd think.
Prompt to paste into Cursor
Add server-side input validation with zod to every API route and server action: check types, lengths and allowed values, and reject anything else with a 400. Make sure no SQL is built with string concatenation. Replace any raw error messages sent to the client with a generic message, and log the details on the server instead.
6. Check your dependencies
AI assistants add packages quickly, and occasionally suggest names that don't exist or aren't what you think. Before launch:
- Run
npm audit(or your package manager's equivalent) and read what it reports before blindly running fixes. - Look over
package.jsonfor anything you don't recognize, and remove unused packages. - Commit your lockfile so builds install the same versions you tested.
- Turn on Dependabot or a similar alert service on your repo, if you use GitHub.
7. Add security headers on your domain
Most frameworks don't send these by default, so they usually need adding in your framework config (e.g. headers() in next.config) or your host. At minimum: Strict-Transport-Security, X-Content-Type-Options: nosniff, X-Frame-Options: DENY (or CSP frame-ancestors), Referrer-Policy: strict-origin-when-cross-origin, and a Content-Security-Policy (start in report-only mode). Make sure http:// redirects to https://. Full guide: security headers.
8. Rate-limit anything that costs money
Login, signup, password reset, and any route that calls an AI, email or SMS API can be hammered by a script. Add rate limits on the server, keyed per signed-in user (and per IP for signed-out routes like login). Set spend limits or usage alerts in your AI provider's dashboard where they exist, so a bug or abuse is caught before it becomes a big bill. Rate limits slow abuse down; they don't make an endpoint safe on their own.
Prompt to paste into Cursor
Add server-side rate limiting to login, signup, password reset and every route that calls a paid API. Limit per signed-in user, and per IP for signed-out routes. Return 429 when the limit is hit. Use a store that works in production on our host (not in-memory if we run multiple instances or serverless).
9. Write a security rule so Cursor doesn't regress
Fixing all this once is great. Keeping it fixed while the AI keeps editing is the hard part. Cursor's project rules live in .cursor/rules as .mdc files (a plain .md there is ignored) and get added to the start of Agent's context. With alwaysApply: true, a rule is included in every chat. Prefer plain markdown? An AGENTS.md in your project root works too.
# .cursor/rules/security.mdc --- description: Security rules for API routes, auth and secrets alwaysApply: true --- - Never put secret keys in client code or in NEXT_PUBLIC_, VITE_ or REACT_APP_ env vars. Read secrets only in server code via process.env. - Every API route / server action must check the signed-in user on the server and confirm they own the resource before reading or changing it. - Validate all request input on the server with a schema (e.g. zod). Never trust client data. - Return generic error messages to the client; log details on the server. - Paid-API endpoints need per-user rate limits; login, signup and password reset need per-IP and per-account limits. Use a shared store, not in-memory, on serverless. - Never disable auth checks, RLS or CORS restrictions to "fix" an error. Explain the error instead. - Don't add new dependencies without saying why.
Commit it so it stays with the repo. Keep in mind rules guide Agent; they don't affect Tab completions, and Cursor's docs say AI guidance shouldn't be your only security control. Still review diffs that touch auth, API routes and env vars, and re-scan after big changes.
The Cursor pre-launch checklist
Copy this into a note or PR description before you share the link.
[ ] No secret keys in client code or NEXT_PUBLIC_ / VITE_ / REACT_APP_ vars [ ] .env files in .gitignore, never committed (rotated if they were) [ ] .cursorignore covers secret files Cursor's defaults miss [ ] Every API route checks who the user is (authentication) [ ] Every API route checks they own the thing (authorization) [ ] Server-side input validation on every endpoint [ ] Generic error messages to the client, details in server logs [ ] npm audit reviewed, unused packages removed [ ] Security headers set on your domain [ ] Rate limits on login, signup and paid-API routes (per user, or per IP/account when signed out) [ ] A security rule in .cursor/rules (or AGENTS.md)
What ChillScan can't see
ChillScan scans your deployed site, not your Cursor project. It reads the page HTML and the first-party JavaScript it loads (without running it), plus response headers, HTTPS, robots.txt and sitemap.xml. It can't see your source code, env vars, git history, .cursorignore or rules, or pages behind a login, so it can't check your auth, authorization, input validation, dependencies or rate limits. Steps 2–6, 8 and 9 are on you — and now you know how. For the full launch list, see the launch checklist.
Let ChillScan do the boring part
Paste your app's URL and the free scan checks your public code for leaked keys, plus headers, HTTPS, SEO and speed. No signup. The $12 deep scan gives you every finding with severity ratings, plain-English explanations and fix tips, plus a copy-paste fix prompt for Cursor to work through the list. One-time payment, no subscription.
What ChillScan checks on Cursor-built sites →
More platform checklists: Lovable security checklist, Bolt.new launch checklist, v0 + Vercel SEO checklist, Replit launch checklist.