Cursor app security checklist: what to check before launch
By StackSiren · Published
Apps built with Cursor run wherever you deploy them — Vercel, Netlify, Render, a VPS — so you own headers, secrets and CORS. Before you share the link, walk this checklist on the live production URL, signed out, the way a stranger would.
Cursor is an editor, not a host. Whatever framework the agent scaffolded (often Vite + React or Next.js), the security surface is the same as any other small web app. Work through each section once. Only test sites and projects you own.
How to run the checks (setup)
- Use the production URL, not the Cursor preview, localhost, or an unmerged preview deploy, unless you are deliberately testing that preview.
- You need a browser and a terminal with
curl(macOS, Windows 10+, Linux). On Windows PowerShell, leave off| head/| grepor use Git Bash / WSL. - Replace
yourapp.comin every example with your real domain. Only test sites and Supabase projects you own — these are ordinary GET requests, the same ones any visitor makes.
Why Cursor apps get this wrong
Cursor agents can scaffold a working UI fast. They do not automatically:
- Put secrets only in server runtimes
- Write
vercel.json/_headers/netlify.tomlsecurity headers - Turn RLS on or write least-privilege policies
- Block
.env/.gitfrom the publish directory
Those are deploy and product decisions. Treat the checklist as a gate before you share the link, the same way you would for hand-written code.
Preview deployments
Preview URLs (Vercel previews, Netlify Deploy Previews, PR environments) are often public unless you turn on protection. Run the secrets and .env checks on a preview once, then again on production after promote. Header rules in vercel.json / netlify.toml usually apply to previews too, which is useful for CSP experiments — just remember to re-check the custom domain after you go live.
1. Secrets: nothing secret in the browser bundle
Anything that reaches the browser is public: HTML, JS bundles, source maps, and every VITE_ / NEXT_PUBLIC_ variable.
Check
- Open the live app → view source (Ctrl+U) and search for
sk_live_,sk-proj-,sk-ant-,sb_secret_,service_role,AKIA,ghp_,xoxb-,SG.,PRIVATE KEY. - In DevTools, search all loaded scripts (Ctrl+Shift+F / Cmd+Option+F) for the same terms.
- Confirm secret keys only live in server code: Netlify / Vercel Functions, Next.js Route Handlers, or Supabase Edge Functions — not in client components.
Pass: only publishable keys appear (Stripe pk_…, Supabase anon / sb_publishable_…, Firebase web config).
Fail: a secret is in the bundle. Rotate it first, then move the call server-side. Full steps: exposed API keys. Related: are Vite env variables secret?, is NEXT_PUBLIC_ safe?, I leaked my OpenAI API key, Supabase service_role key exposed.
Our exposed API key checker searches the live bundle on sites you own.
2. Headers: CSP, HSTS, clickjacking, nosniff
Hosts do not invent a full header set for you. Confirm what production actually sends.
Check
curl -sI https://yourapp.com/ | grep -iE 'strict-transport|x-content-type|x-frame|referrer-policy|permissions-policy|content-security'
You want at least Strict-Transport-Security, X-Content-Type-Options: nosniff, clickjacking protection (X-Frame-Options or CSP frame-ancestors), and a Referrer-Policy. Prefer a Content-Security-Policy once you have rolled it out carefully.
Where to set them
- Vercel / Next.js / v0:
vercel.jsonornext.config— see add security headers on Vercel - Netlify / many Bolt exports:
_headersornetlify.toml— see add security headers on Netlify - Other hosts: set headers in the reverse proxy or framework middleware
Guide: security headers. Tool: security headers checker.
3. .env and .git must not be public
A readable .env or .git folder means secrets and history may be downloadable.
Check
curl -s https://yourapp.com/.env | head -5
curl -s https://yourapp.com/.git/config | head -5
| You see | Meaning |
|---|---|
| 404, 403, or your normal SPA homepage HTML | Usually fine |
Lines like SUPABASE_URL= or STRIPE_SECRET_KEY= |
.env is public — rotate everything in it |
A block starting with [core] |
.git is public |
Then remove the files from the deploy (.gitignore, host ignore files, publish-directory hygiene) and rotate. Guide: exposed .env / .git. How-to: is my .env file public?. Tool: env file checker.
Also turn off public production source maps if they are on: disable Vite source maps.
4. CORS: no wildcard-with-credentials
CORS headers relax the browser's same-origin rules. A site-wide Access-Control-Allow-Origin: * on cookie-authenticated APIs is a red flag; reflecting any Origin while allowing credentials is worse.
Check
curl -sI -H "Origin: https://evil.example" https://yourapp.com/api/whatever | grep -i access-control
Pass: either no CORS headers (same-origin only) or an explicit allowlist that does not echo arbitrary origins when credentials are on.
Fail: Access-Control-Allow-Origin mirrors the evil origin and Access-Control-Allow-Credentials: true.
Guide: CORS misconfiguration. Do not "fix" APIs by pasting * into _headers or vercel.json for every path.
5. Auth and data access (especially Supabase)
If the app uses Supabase (common with Cursor + Bolt / Lovable-style stacks):
- Confirm RLS is on for every user-facing table. Policies should not be
USING (true)for sensitive data — see USING (true) policies. - Never ship the
service_rolekey to the client — service_role exposed. - New tables need explicit GRANTs as well as RLS; a bare table often returns
42501 permission denied— fix 42501. - Test signed out and as two different users. Each should only see their own rows.
- Keep the anon / publishable key only if RLS (and grants) actually protect the data — Bolt RLS checklist, Lovable RLS policies.
Signed-out API probe (example for a Supabase REST table you own):
curl -s "https://YOUR-PROJECT.supabase.co/rest/v1/YOUR_TABLE?select=*&limit=5" \
-H "apikey: YOUR_ANON_OR_PUBLISHABLE_KEY" \
-H "Authorization: Bearer YOUR_ANON_OR_PUBLISHABLE_KEY"
An empty array or a clear permission error when signed out can be fine, depending on your product. Rows of other users' data are not. Fix policies and grants before launch; do not rely on hiding UI.
For non-Supabase backends, the same idea applies: every API that returns user data must check a session server-side. Client-only "hide the admin button" checks are not access control.
Launch pass / fail summary
| Area | Quick pass signal |
|---|---|
| Secrets | No sk_, service_role, AKIA, etc. in view-source or script search |
| Headers | curl -I shows HSTS, nosniff, frame protection, referrer policy |
.env / .git |
Bodies are not real env lines or git config |
| CORS | No reflected Origin + credentials |
| Auth / RLS | Signed-out and second-user tests match the policy you intended |
When every row passes, share the link. Re-run the same checks after the next deploy that touches env, headers or auth.
Related reading
- Add security headers on Vercel
- Add security headers on Netlify
- Is my .env file public?
- Disable Vite production source maps
FAQ
Does Cursor add security headers or hide secrets for me? No. Cursor writes and edits code on your machine. The host and the code you deploy decide headers, env exposure and CORS.
I deployed to Vercel from a Cursor repo. Is that enough?
Vercel documents HSTS on its platform domains; the rest of the header set, secret placement and CORS are still yours. Use vercel.json or next.config and verify with curl -I on the custom domain.
What about Netlify?
Same story: configure _headers or [[headers]], keep secrets out of VITE_ / client code, and confirm on the live URL. See the Netlify headers post.
How long should this take? About 20–30 minutes the first time if you already know where the project deploys. Budget longer if you find a leaked key (rotate first) or missing RLS.
Can I automate the outside checks?
Yes. StackSiren's first scan is free and covers exposed keys, headers, .env/.git-style paths and related findings on sites you own. Paid plans start at Solo ($29/mo) with scheduled rescans. Never scan someone else's site without permission.
Do I need Deep Scan before launch? A free passive scan covers what a stranger can see from outside (keys in bundles, headers, public files). Ownership-verified Deep Scan ($49 one-off, or on Pro) goes further when you need authenticated checks. Start with the free scan and this checklist; escalate if something fails.
What if I only shipped a static marketing site from Cursor?
You still need headers, no public .env/.git, and no secrets in client JS. Skip the Supabase rows if you have no backend. Re-run section 1–3 after every deploy that changes env or build config.
Fix guides for this topic
Platform checklists: Cursor · Vercel · v0