Configure Cursor to respect your dev, QA, and production environments by adding environment-aware rules to .cursorrules, referencing .env files with @context, and using project rules that match environment-specific config files. This prevents Cursor from hardcoding URLs, mixing up API keys, or generating code that only works in one environment.
Why Cursor Needs Environment Context
By default, Cursor has no idea whether your project targets local development, QA, staging, or production. It will happily hardcode localhost URLs, inline API keys, or generate code that assumes a single environment. This tutorial shows you how to teach Cursor about your environment strategy so every suggestion uses process.env, respects .env files, and never leaks credentials into generated code. It is designed for any developer who manages multiple deployment targets.
Prerequisites
- Cursor installed (Free or Pro)
- A project with at least one .env file
- Basic understanding of environment variables in your language
- Git initialized in your project root
Step-by-step guide
Create a .cursorrules file with environment conventions
Create a .cursorrules file with environment conventions
Add a .cursorrules file to your project root that tells Cursor how your project handles environments. This file is read automatically on every prompt. Specify that all configuration must come from environment variables, never hardcoded. List your environment names and the .env file naming pattern you follow.
1# .cursorrules23## Environment handling4- NEVER hardcode URLs, API keys, database strings, or secrets5- ALWAYS use process.env.VARIABLE_NAME for configuration6- Environment files follow the pattern: .env.local, .env.development, .env.production7- When generating config, include fallback defaults for local development only8- Use this pattern: const apiUrl = process.env.API_URL || 'http://localhost:3000'910## Environment variable naming11- Prefix client-safe vars with NEXT_PUBLIC_ (Next.js) or VITE_ (Vite)12- Server-only secrets: DB_URL, API_SECRET, JWT_SECRET13- Never expose server-only vars to client bundlesPro tip: Cursor reads .cursorrules on every prompt, but rules can drift in long sessions. If Cursor starts hardcoding values again, start a fresh chat with Cmd+N.
Expected result: Cursor will reference these rules in every code suggestion and avoid hardcoding environment-specific values.
Create an environment template file for context
Create an environment template file for context
Create a .env.example file that lists all required environment variables with placeholder values. This file is safe to commit to Git and gives Cursor a reference for which variables exist without exposing real secrets. Add your .env files to .cursorignore so Cursor never indexes real credentials.
1# .env.example2API_URL=http://localhost:30003DATABASE_URL=postgresql://user:password@localhost:5432/mydb4REDIS_URL=redis://localhost:63795JWT_SECRET=your-secret-here6NEXT_PUBLIC_APP_NAME=MyApp7NEXT_PUBLIC_ANALYTICS_ID=UA-000000-0Pro tip: Add .env, .env.local, .env.production to your .cursorignore file so Cursor never accidentally reads or indexes real secrets.
Expected result: A committed template that Cursor can safely reference for variable names and structure.
Add real .env files to .cursorignore
Add real .env files to .cursorignore
Create or update your .cursorignore file to exclude all real environment files from Cursor's indexing and AI analysis. This prevents any possibility of secrets being sent to the AI model. The .cursorignore file works like .gitignore syntax.
1# .cursorignore2.env3.env.local4.env.development5.env.production6.env.staging7*.pem8*.key9credentials.jsonExpected result: Cursor will never index or read your actual environment files, even when using @codebase search.
Use @file context to reference the env template
Use @file context to reference the env template
When asking Cursor to generate code that needs configuration, reference the .env.example file explicitly using @file context. Open Chat with Cmd+L or Composer with Cmd+I, then type your prompt referencing the template. This gives Cursor the exact variable names it should use.
1// Prompt to type in Cursor Chat (Cmd+L):2// @.env.example Generate a database connection module that reads3// all config from environment variables. Include connection pooling4// and a health check function.Pro tip: You can also reference @Folders to give Cursor visibility into your config/ directory structure without exposing individual secret files.
Expected result: Cursor generates a database module that uses process.env.DATABASE_URL and other variables matching your .env.example exactly.
Create a project rule for config files
Create a project rule for config files
Create a .cursor/rules/env-config.mdc file that auto-attaches whenever Cursor encounters configuration-related files. Use glob patterns to match config files so the rule applies automatically. This ensures environment rules are enforced even when you forget to mention them in your prompt.
1---2description: Environment configuration rules3globs: "*.config.{js,ts,mjs}, .env.*, **/config/**"4alwaysApply: false5---67- All configuration values MUST come from environment variables8- Provide sensible localhost defaults for development9- Use zod or joi to validate env vars at startup10- Never log environment variable values, only log variable names11- Pattern: `const config = { dbUrl: z.string().parse(process.env.DATABASE_URL) }`12- Reference @.env.example for the full list of expected variablesPro tip: Use the glob pattern to match all config-related files. The rule activates automatically when you're editing those files, even without manually referencing it.
Expected result: When editing any config file, Cursor automatically loads these environment rules without manual prompting.
Prompt Cursor to generate an environment validation module
Prompt Cursor to generate an environment validation module
Use Composer (Cmd+I) to generate a startup validation module that checks all required environment variables are present. Reference your .env.example and .cursorrules for context. This module runs at application startup and fails fast if any required variable is missing.
1// Prompt to type in Cursor Composer (Cmd+I):2// @.env.example @.cursorrules3// Generate an env.ts module that:4// 1. Validates all env vars from .env.example are present at startup5// 2. Uses zod for type-safe validation6// 3. Exports a typed config object7// 4. Throws a clear error listing ALL missing vars, not just the first one8// 5. Separates server-only and client-safe variablesPro tip: This is the single most impactful pattern for environment safety. Once this module exists, Cursor will import from it instead of accessing process.env directly in generated code.
Expected result: A typed env.ts module that validates all environment variables at startup and exports a fully typed config object.
Complete working example
1import { z } from 'zod';23const serverSchema = z.object({4 DATABASE_URL: z.string().url(),5 REDIS_URL: z.string().url().optional(),6 JWT_SECRET: z.string().min(32),7 API_URL: z.string().url().default('http://localhost:3000'),8 NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'),9});1011const clientSchema = z.object({12 NEXT_PUBLIC_APP_NAME: z.string().default('MyApp'),13 NEXT_PUBLIC_ANALYTICS_ID: z.string().optional(),14});1516function validateEnv() {17 const serverResult = serverSchema.safeParse(process.env);18 const clientResult = clientSchema.safeParse(process.env);1920 const errors: string[] = [];2122 if (!serverResult.success) {23 errors.push(24 ...serverResult.error.issues.map(25 (issue) => `SERVER ${issue.path.join('.')}: ${issue.message}`26 )27 );28 }2930 if (!clientResult.success) {31 errors.push(32 ...clientResult.error.issues.map(33 (issue) => `CLIENT ${issue.path.join('.')}: ${issue.message}`34 )35 );36 }3738 if (errors.length > 0) {39 throw new Error(40 `Environment validation failed:\n${errors.join('\n')}`41 );42 }4344 return {45 server: serverResult.data!,46 client: clientResult.data!,47 };48}4950export const env = validateEnv();51export type ServerEnv = z.infer<typeof serverSchema>;52export type ClientEnv = z.infer<typeof clientSchema>;Common mistakes when making Cursor Respect Dev and Prod Environments
Why it's a problem: Adding real .env files to Cursor's context with @file
How to avoid: Only reference .env.example with placeholder values. Add real .env files to .cursorignore.
Why it's a problem: Forgetting to restart Cursor after changing .cursorrules
How to avoid: Start a new chat session with Cmd+N after updating rules, or restart Cursor entirely.
Why it's a problem: Using NEXT_PUBLIC_ prefix for server-only secrets
How to avoid: Add a rule to .cursorrules explicitly listing which prefixes are client-safe and which variables must remain server-only.
Why it's a problem: Not validating environment variables at startup
How to avoid: Generate a validation module using the Composer prompt in Step 6 and import it in your application entry point.
Best practices
- Always commit .env.example to Git with placeholder values so Cursor and teammates know which variables are needed
- Use .cursorignore to exclude all real .env files, private keys, and credential files from AI indexing
- Create a typed environment validation module that runs at startup and fails fast with clear error messages
- Reference @.env.example in prompts instead of describing variables by memory to ensure accuracy
- Use .cursor/rules/ with glob patterns to auto-attach environment rules when editing config files
- Separate client-safe and server-only variables with clear naming conventions in your .cursorrules
- Start a new chat session after changing environment rules to ensure Cursor picks up the latest version
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to set up environment variable handling for a [framework] project with local, staging, and production environments. Generate: 1) A .env.example with all common variables, 2) A TypeScript validation module using zod, 3) A config module that exports typed environment values. All config must come from process.env with local defaults.
@.env.example @.cursorrules Generate an environment configuration module that validates all variables from .env.example using zod, exports typed server and client config objects, and throws a descriptive error listing all missing variables at startup. Use process.env for all values with localhost fallbacks for development.
Frequently asked questions
Does Cursor send my .env file contents to its servers?
Cursor sends referenced file contents to AI models for processing. If you use @file on a real .env file, those secrets are transmitted. Add .env files to .cursorignore and only reference .env.example with placeholder values.
How do I manage environment variables and secrets for a Cursor project?
Create a .env.example with placeholder values and commit it to Git. Add all real .env files to .cursorignore. Define environment rules in .cursorrules that enforce process.env usage. Generate a typed validation module to catch missing variables at startup.
Can Cursor automatically detect which environment I am working in?
Cursor does not detect your runtime environment. You need to tell it through .cursorrules which environments exist and how they differ. Reference your NODE_ENV or similar variable in rules so Cursor generates environment-conditional code.
Why does Cursor keep hardcoding localhost URLs in my code?
Cursor defaults to hardcoded values when it has no environment context. Add a .cursorrules rule stating all URLs must come from environment variables, and reference your .env.example file in prompts so Cursor knows the correct variable names.
Should I use .cursorrules or .cursor/rules/ for environment settings?
Use .cursor/rules/ with .mdc files for environment settings. They support glob patterns that auto-attach when editing config files, making them more reliable than the legacy .cursorrules file which requires manual referencing.
How do I prevent Cursor from mixing up development and production API keys?
Define a clear naming convention in your .cursorrules (e.g., API_URL for the base, with environment-specific .env files). Generate a validation module that checks the current NODE_ENV and validates that the correct set of variables is present.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation