Skip to main content
RapidDev - Software Development Agency
cursor-tutorial

How to Make Cursor Respect Dev and Prod Environments

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.

What you'll learn

  • How to create .cursorrules that enforce environment-aware code generation
  • How to safely reference .env files without leaking secrets to the AI
  • How to use @context to give Cursor environment-specific knowledge
  • How to set up project rules that auto-attach for config files
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read10-15 minCursor Pro+, any language/frameworkMarch 2026RapidDev Engineering Team
TL;DR

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

1

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.

.cursorrules
1# .cursorrules
2
3## Environment handling
4- NEVER hardcode URLs, API keys, database strings, or secrets
5- ALWAYS use process.env.VARIABLE_NAME for configuration
6- Environment files follow the pattern: .env.local, .env.development, .env.production
7- When generating config, include fallback defaults for local development only
8- Use this pattern: const apiUrl = process.env.API_URL || 'http://localhost:3000'
9
10## Environment variable naming
11- Prefix client-safe vars with NEXT_PUBLIC_ (Next.js) or VITE_ (Vite)
12- Server-only secrets: DB_URL, API_SECRET, JWT_SECRET
13- Never expose server-only vars to client bundles

Pro 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.

2

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.

.env.example
1# .env.example
2API_URL=http://localhost:3000
3DATABASE_URL=postgresql://user:password@localhost:5432/mydb
4REDIS_URL=redis://localhost:6379
5JWT_SECRET=your-secret-here
6NEXT_PUBLIC_APP_NAME=MyApp
7NEXT_PUBLIC_ANALYTICS_ID=UA-000000-0

Pro 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.

3

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.

.cursorignore
1# .cursorignore
2.env
3.env.local
4.env.development
5.env.production
6.env.staging
7*.pem
8*.key
9credentials.json

Expected result: Cursor will never index or read your actual environment files, even when using @codebase search.

4

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.

Cursor Chat prompt
1// Prompt to type in Cursor Chat (Cmd+L):
2// @.env.example Generate a database connection module that reads
3// all config from environment variables. Include connection pooling
4// 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.

5

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.

.cursor/rules/env-config.mdc
1---
2description: Environment configuration rules
3globs: "*.config.{js,ts,mjs}, .env.*, **/config/**"
4alwaysApply: false
5---
6
7- All configuration values MUST come from environment variables
8- Provide sensible localhost defaults for development
9- Use zod or joi to validate env vars at startup
10- Never log environment variable values, only log variable names
11- Pattern: `const config = { dbUrl: z.string().parse(process.env.DATABASE_URL) }`
12- Reference @.env.example for the full list of expected variables

Pro 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.

6

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.

Cursor Composer prompt
1// Prompt to type in Cursor Composer (Cmd+I):
2// @.env.example @.cursorrules
3// Generate an env.ts module that:
4// 1. Validates all env vars from .env.example are present at startup
5// 2. Uses zod for type-safe validation
6// 3. Exports a typed config object
7// 4. Throws a clear error listing ALL missing vars, not just the first one
8// 5. Separates server-only and client-safe variables

Pro 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

src/config/env.ts
1import { z } from 'zod';
2
3const 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});
10
11const clientSchema = z.object({
12 NEXT_PUBLIC_APP_NAME: z.string().default('MyApp'),
13 NEXT_PUBLIC_ANALYTICS_ID: z.string().optional(),
14});
15
16function validateEnv() {
17 const serverResult = serverSchema.safeParse(process.env);
18 const clientResult = clientSchema.safeParse(process.env);
19
20 const errors: string[] = [];
21
22 if (!serverResult.success) {
23 errors.push(
24 ...serverResult.error.issues.map(
25 (issue) => `SERVER ${issue.path.join('.')}: ${issue.message}`
26 )
27 );
28 }
29
30 if (!clientResult.success) {
31 errors.push(
32 ...clientResult.error.issues.map(
33 (issue) => `CLIENT ${issue.path.join('.')}: ${issue.message}`
34 )
35 );
36 }
37
38 if (errors.length > 0) {
39 throw new Error(
40 `Environment validation failed:\n${errors.join('\n')}`
41 );
42 }
43
44 return {
45 server: serverResult.data!,
46 client: clientResult.data!,
47 };
48}
49
50export 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.

ChatGPT Prompt

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.

Cursor Prompt

@.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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.