Guides

Is my Cursor app secure?

Cursor is the editor on your machine. It does not host the app, and it does not pick a backend. The leaks show up in whatever you asked it to build, usually after a prompt like "just make the query work."

Free check

Check the live app

Paste the public Cursor URL. The check reads what a visitor's browser can already see, then gives you a private report with a fix prompt for each issue.

Free, one time, and limited to what a stranger can already see. No account required.

What Cursor actually ships

Cursor can write any stack you point it at. A lot of shipped apps are Next.js deployed on Vercel, or a Vite app, often with Supabase or Firebase added in the same chat. Those are common choices, not a rule. The checks below follow the code that actually got deployed: client bundles, env prefixes, database rules, and files the host serves.

Mistakes worth checking

These are the ones that show up in apps built with Cursor. Each step is something you can do in the browser or in the product dashboard.

  1. Critical

    NEXT_PUBLIC_ or VITE_ on a real secret

    Next.js inlines every NEXT_PUBLIC_ variable into the client bundle. Vite inlines every VITE_ variable. Cursor will keep the prefix you, or a sample, already used. A service role key, a Stripe secret, or an OpenAI key with either prefix is public on deploy.

    Check it yourself

    Search the repo for NEXT_PUBLIC_ and VITE_. Then search the live JavaScript for service_role, sk-, sk_live, and rk_. A name that is only in a server file, with no public prefix, is the pattern you want.

  2. High

    Row-level security turned off to clear an error

    The first Supabase error in a Cursor chat is often a permission error. A fast fix is to disable row-level security or to add a policy of true. The feature starts working. The table is then readable with the anon key.

    Check it yourself

    In the Supabase dashboard, every table in the public schema should have row-level security on. Open each policy. Private data should require auth.uid() to match the row's user. Deploy again only after that is true.

  3. Critical

    A committed .env that the host serves

    Local tools read .env so you can develop. If that file is committed, or copied into a public folder, some hosts will serve it at /.env. Values belong in the host's environment settings.

    Check it yourself

    Open https://your-domain/.env, /.env.local, and /.env.production. A download is a leak. Remove the file from the deploy and rotate every value that was in it.

  4. High

    Firebase left in test mode

    Firebase test rules are open so you can prototype. They allow reads and writes without a login. Agents scaffold them and then ship the app. The public web API key in the page is expected. The rules are the lock.

    Check it yourself

    In the Firebase console, Realtime Database rules should not set ".read" or ".write" to true. Firestore rules should require request.auth, then a tighter match on the user id. Publish the rules and try the data URL while signed out.

  5. Medium

    Production source maps left on

    Next.js stays quiet about browser source maps unless productionBrowserSourceMaps is enabled. Vite publishes them when build.sourcemap is on. Either setting can hand a visitor your original files.

    Check it yourself

    View a production script from your domain and search for sourceMappingURL. Open the map. If it contains sourcesContent or your original paths, turn the setting off and delete the .map files.

A prompt you can paste

Paste this into Cursor, publish again, and run the check a second time. If a key was already on the live site, rotate it in the vendor dashboard. The prompt cannot do that part.

Audit this project for secrets and open data that a visitor can already see. Change the code, and do not disable a safeguard to make a page load.

1. Remove from client components, and from any NEXT_PUBLIC_ or VITE_ variable: Supabase service_role keys, Stripe secret or restricted keys (sk_ and rk_), OpenAI and Anthropic keys, AWS and GitHub tokens, private keys, and database URLs with passwords. The Supabase anon key and a Stripe publishable key (pk_) may stay in the browser.
2. Put provider calls and service-role work in server-only code. On Next.js, use a Server Action or a route handler that reads a normal environment variable. On Vite, use a server function, not a VITE_ variable.
3. Enable Supabase row-level security on every public table and replace any policy of true. For Firebase, remove test-mode rules and require auth for reads and writes.
4. Stop the host from serving .env, .git, and source maps. In Next.js leave productionBrowserSourceMaps off. In Vite set build.sourcemap to false for production.
5. List the key names that were exposed so I can rotate them. Do not print secret values.

After the edits, tell me the files you changed.

What the check looks at

  • The public page and the JavaScript it loads, for common leaked secret keys: a Supabase service role key, OpenAI, Anthropic, Stripe secret keys, AWS, GitHub, Slack, private keys, and database URLs that include a password. A Supabase anon key is called out as expected in the browser.
  • A short list of public paths: /.env, /.env.local, /.env.production, /.git/config, /.git/HEAD, and /.npmrc.
  • A source map, only when a script on the page already points at one, and only when that map includes original source.
  • Supabase tables, only when the page already includes a supabase.co address and the public anon key. It asks for one row per table, then drops that row.
  • Firebase Realtime Database and Firestore, only when the public config is already in the page. It uses that public config and does not sign in.
  • A few browser safety headers on the main response. Missing headers are noted when two or more of the watched ones are absent. They are less urgent than a leaked key.

What stays outside it

  • It reads. It does not log in, submit forms, or change data.
  • It does not guess passwords, fuzz inputs, or hunt through hidden paths.
  • It does not use any secret it finds, including a service role key.
  • It does not open your builder's dashboard. A rule that lives only there can stay invisible.
  • A clear result describes this public look. It is not a certificate that the whole app is safe.
  • Cursor is the editor. The check looks at the URL you deployed, not at the chat transcript or the files that never got published.

Questions

Free check

Run it on the published URL

Use the address a customer would type. The report stays private, and the shareable scorecard hides the details.

Free, one time, and limited to what a stranger can already see. No account required.

Other builders

Vibe-coded app security checklist · All guides