ShipWatchFree scan

Cursor put your API key in the code? Find it, move it, stop it recurring

Ask an AI editor to "just make the API call work" and it may paste your key straight into a source file. Once that file is committed or deployed, the key is out. This page covers finding every copy, moving it, and stopping the agent from doing it again.

Short answer: Search your files and git history for key prefixes, rotate anything that was committed or deployed, and move the value to an environment variable read only by server code. Then add a Cursor project rule that forbids inlining secrets, and turn on a pre-commit or push check as a backstop.

This guide is about the repo. If the key may already be public, start with the rotate-first playbook, then come back.

Step 1: Find it in your files [manual]

From the project root, search for common key prefixes:

grep -rnE "sk-proj-|sk_live_|rk_live_|sk-ant-|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|sb_secret_" \
  --exclude-dir=node_modules --exclude-dir=.git .

Also look for quoted strings next to names like apiKey, Authorization or Bearer. AI editors often paste keys as defaults, such as process.env.OPENAI_API_KEY || "sk-proj-...". That fallback is just as leaked as a plain string.

Step 2: Find it in git history [manual]

Deleting the line doesn't remove earlier commits. Check them:

git log -p --all -S "sk-proj-"
gitleaks git -v .

The first finds commits that added or removed the string. Gitleaks scans every commit for many key formats; gitleaks dir -v . scans the working folder instead. Any hit in a commit that was pushed means the key is leaked, even if the repo is private, because anyone with access can read it.

Step 3: Check what shipped [manual]

A key in a frontend file is in your bundle. Build and search the output (dist/ for Vite, .next/static/ for Next.js), or open your live site's scripts in DevTools and search for the first characters of the key.

Step 4: Move it

  1. Rotate the key if it was committed or deployed [manual].
  2. Put the new value in .env.local (and make sure .env* is in .gitignore), plus your host's environment settings.
  3. Read it only in server code: a Route Handler, an API route, an edge function. Don't add a VITE_ or NEXT_PUBLIC_ prefix; see the VITE_ and NEXT_PUBLIC_ guides.
  4. Add a .env.example with empty values so the agent knows which names exist.
Search this repository for hardcoded API keys and tokens, including fallback values like process.env.X || "...". Replace each with a read from a server-only environment variable, move any client-side use into a server route, add the variable names with empty values to .env.example, and confirm .env* files are in .gitignore. List every key you found so I can rotate it.

Step 5: Stop it recurring

A Cursor project rule

Cursor reads project rules from .cursor/rules as .mdc files with front matter. Create .cursor/rules/no-secrets.mdc:

---
description: Handling API keys and secrets
alwaysApply: true
---
- Never write an API key, token or password into source code, tests or fallbacks.
- Read secrets from environment variables in server-side code only.
- Never prefix a secret with VITE_ or NEXT_PUBLIC_.
- If a secret is missing, add its name to .env.example and tell me; don't invent one.

alwaysApply: true includes it in every Agent chat. A plain .md file in that folder is ignored; Cursor also reads an AGENTS.md in the project root if you prefer plain markdown.

A backstop that doesn't depend on the AI

If your project folder was ever deployed whole, also check whether your .git folder is public.

What the free scan checks here (and what it doesn't)

The scan reads your live HTML and up to 25 linked scripts and flags Stripe sk_live_/rk_live_, OpenAI, Anthropic, AWS AKIA/ASIA, Google AIza, and Supabase service_role and sb_secret_ keys. It also checks /.env, /.git/HEAD, HTTPS and its redirect, and the CSP, HSTS, X-Frame-Options or frame-ancestors, and nosniff headers.

It doesn't read your repository or its history, so a key that's committed but never deployed won't show up. Steps 1 and 2 cover that [manual].

FAQ

Will a Cursor rule guarantee the agent never hardcodes a key?
No. A rule is added to the agent's context, which makes it far less likely, but it's guidance, not enforcement. Cursor's own docs say AI guidance shouldn't be your only security control. Keep a scanner like gitleaks or push protection as the backstop.
Does GitHub block pushes that contain API keys?
GitHub's push protection for users is on by default and blocks many known secret types when you push to public repositories. For private repositories, push protection has to be enabled at the repository level and needs GitHub Secret Protection.
The key was only in a local file I never committed. Do I need to rotate it?
If it never left your machine, was never committed, and never shipped in a deployed build, moving it to an environment variable is enough. If any of those happened, rotate it.

Scan your app free in 30 seconds

Paste your live URL for a plain-English report with fix prompts. If you keep shipping, weekly monitoring re-checks it: Solo $19/mo or Pro $49/mo.

Scan my app free