Cursor Indexing: Tune It for Speed and Accuracy

How Cursor's codebase index actually works, the .cursorignore vs .cursorindexingignore split, and the tuning loop that stops wrong-file references — verified June 2026.

TL;DR

A well-tuned Cursor index is what separates “Cursor understands my repo” from “Cursor keeps citing files that do not exist.” Three moves fix 90% of indexing pain: (1) keep build output, caches, and secrets out of the index with .cursorignore / .cursorindexingignore; (2) re-index after big repo changes instead of trusting the 5-minute auto-sync; (3) pin context explicitly with @File / @Folder / @Symbol so the agent stops guessing. Everything below is the actual loop, verified against Cursor’s docs as of June 2026.

How Cursor’s index actually works (so the fixes make sense)

Cursor builds a semantic index, not a plain text search. When you open a workspace it splits your code into logical chunks (functions, classes, blocks) using tree-sitter, turns each chunk into a vector embedding, and stores those vectors in a remote vector database. At query time your question becomes a vector too, and the closest chunks are pulled into context. Semantic search becomes available at roughly 80% completion, and the index then auto-syncs every 5 minutes, re-embedding only the files that changed (Cursor compares a Merkle-tree root hash to find them).

Two consequences matter for tuning:

  • Stale index = wrong files, not an error. If the embedding for a file is old or the file was never indexed, Cursor pulls the nearest chunk it has — which often looks like a confident hallucination. The model did not fail; search did.
  • Source code is not stored in plaintext. Per Cursor’s docs, code is held in memory during indexing then discarded; embeddings are stored without filenames or raw source, and file paths are encrypted in transit. That is reassuring, but it is not a reason to index secrets (see the security section).

The one distinction most people get wrong

There are two ignore files, and they do different things. Mixing them up is the single most common reason “I ignored it but Cursor still touches it.”

FileWhat it blocksUse it for
.cursorignoreAll AI access — indexing, Tab, Agent, Inline Edit, and @ mentions. The file is invisible to Cursor.Secrets, anything the AI should never read.
.cursorindexingignoreIndexing only. Files stay readable by the agent and @-mentionable, but never show up in semantic search.Large generated code you sometimes open but never want polluting search. Added in Cursor 0.46.

Both use .gitignore syntax (*, **, ?, and ! negation). Cursor also automatically honors your .gitignore plus a built-in default list that already covers node_modules, .git, __pycache__, .next, lock files, binaries, and .env*. So most “obvious” ignores are redundant — your job is the project-specific noise the defaults miss.

A .cursorignore baseline that earns its place

Because the defaults already cover node_modules, lock files, and .env*, keep your file focused on your noise — generated output and large data that lives inside source paths:

# Generated / build output that ends up in source dirs
dist/
build/
coverage/
.astro/
storybook-static/
public/assets/        # only if these are generated
src/generated/        # codegen output

# Heavy data that bloats embeddings
data/large/
*.parquet
fixtures/**/*.json     # large customer-data fixtures

# Extra secret locations beyond the default .env*
secrets/
**/credentials.json
**/service-account*.json

Commit this file so teammates inherit the tuning. For a monorepo, you can rely on hierarchical ignore (enable “Hierarchical Cursor Ignore” in settings) so a .cursorignore inside packages/web/ governs just that package.

Security: ignoring a file is not the same as protecting it

Add .env, secrets/, key files, and credential JSON to .cursorignore immediately. But know the limit, because community reports keep surfacing leaks: a .cursorignore entry stops a file from being indexed, but the Agent can still open it with its file-system tool when it decides it needs to. If a path holds real secrets, move them out of the working tree entirely — into environment variables or a secrets manager — and do not rely on the ignore file alone. Treat .cursorignore as noise control, not access control.

The tuning loop, step by step

  1. Check status first. Cursor Settings → Indexing & Docs. Note whether it says Indexing, Synced, or Failed, and use “View included files” to confirm what is actually in the index. If your secrets or dist/ show up there, your ignore rules are not taking effect.
  2. Add your .cursorignore before the first index, not after. Once junk is embedded it sticks around until you re-index.
  3. Re-index after big changes. Large merges, branch switches with significant divergence, and dependency upgrades all stale the index faster than the 5-minute sync can catch gracefully. Cursor Settings → Indexing → re-index. A clean medium project (a few thousand files) should finish in well under 2 minutes.
  4. Pin context explicitly when it matters. Instead of “fix the auth bug,” write “fix the bug in @File:src/auth/login.ts.” @File, @Folder, and @Symbol bypass semantic ambiguity entirely — the content is injected directly, no search involved.
  5. Scope a monorepo by workspace. For a million-file monorepo, open the specific package (e.g. packages/web) rather than the repo root so indexing stays fast and search stays relevant.

When indexing is stuck or slow: a diagnostic checklist

Work top to bottom; the first hit usually explains it.

  • Is node_modules (and other build output) actually excluded? Confirm via “View included files.” Excluding it commonly drops a cold index from minutes to under a minute.
  • Any single file over a few MB? Huge single files (bundled vendor JS, generated schemas, minified maps) choke chunking. Split, gzip, or ignore them.
  • Symlinks pointing outside the repo? Cursor can follow them and try to index your home directory. Remove or ignore.
  • Generated files mixed into src/? Move them out or add them to .cursorindexingignore.
  • Stuck on “Setting up indexing…” or “Resyncing index” for 10+ minutes on a small repo? That is not normal patience territory — it is usually a corrupt local cache, an auth hiccup, or an OS file-watcher limit. See why indexing never completes and Cursor stuck indexing for the cache-clear and resync steps.

For reference, a genuinely large repo (10K+ files) can legitimately take 10–30 minutes on its first index. After that, incremental sync should be near-instant — if it is not, something is wrong.

A 5-minute first run to validate your setup

  1. Open Cursor Settings → Indexing & Docs and read the status.
  2. Add the baseline .cursorignore above, then re-index and time it. Compare against the View-included-files list — your generated dirs should be gone.
  3. Open Agent/Composer and reference a known file with @File. Confirm its content lands in context. If it does not, an ignore rule excluded it by accident.
  4. Run a small Inline Edit that names a non-trivial symbol. Cursor should resolve it without inventing a signature. A hallucinated symbol name means the index is missing that file.

Common mistakes

  • Confusing the two ignore files. Putting a secret in .cursorindexingignore leaves it fully readable by the Agent. Secrets go in .cursorignore (and ideally out of the repo).
  • Never re-indexing after a refactor that moves files. Old paths linger in the index and produce wrong references that look like model failures.
  • Trusting the index for cross-file edits. It is good, not telepathic — @-mention your central configs, key types, and main entrypoint in any non-trivial prompt.
  • Re-listing the defaults. node_modules, .git, lock files, and .env* are already ignored; padding your .cursorignore with them just hides the entries that actually matter.
  • Conflicting .cursorrules across project / workspace / user scope. This shows up as “instructions ignored,” never as an error (Cursor config conflict).

FAQ

  • Does my code leave my machine?: Embeddings are computed and stored on Cursor’s remote infrastructure (a vector database), but per Cursor’s docs the raw source is held in memory only during indexing and then discarded, embeddings are stored without filenames or source, and paths are encrypted in transit. Check the current privacy and security docs before indexing regulated code.
  • .cursorignore vs .cursorindexingignore — which do I want?: .cursorignore makes a file invisible to every AI feature; .cursorindexingignore only keeps it out of semantic search while leaving it readable and @-mentionable. Use the first for secrets, the second for large files you occasionally open.
  • Do I have to re-index after every commit?: No. Cursor auto-syncs every 5 minutes and re-embeds only changed files. Manual re-index is for large structural changes (big merges, branch switches, dependency upgrades) where you do not want to wait for drift to resolve.
  • Why does Cursor still miss a file after indexing?: One of three things — it was ignored, the index is stale, or your prompt did not pin enough context. Verify with @File: if the content shows up, search was the problem, not the model.
  • How big a repo can Cursor index?: Tens of thousands of files is fine with a clean .cursorignore; first index of a 10K+ file repo can take 10–30 minutes. Million-file monorepos should be opened per-workspace rather than at the root.

Tags: #AI coding #Tutorial #Cursor