# How to Make Cursor Respect Dev and Prod Environments

- Tool: Cursor
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Cursor Pro+, any language/framework
- Last updated: March 2026

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

## Before you start

- 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

## Environment handling
- NEVER hardcode URLs, API keys, database strings, or secrets
- ALWAYS use process.env.VARIABLE_NAME for configuration
- Environment files follow the pattern: .env.local, .env.development, .env.production
- When generating config, include fallback defaults for local development only
- Use this pattern: const apiUrl = process.env.API_URL || 'http://localhost:3000'

## Environment variable naming
- Prefix client-safe vars with NEXT_PUBLIC_ (Next.js) or VITE_ (Vite)
- Server-only secrets: DB_URL, API_SECRET, JWT_SECRET
- 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
API_URL=http://localhost:3000
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
REDIS_URL=redis://localhost:6379
JWT_SECRET=your-secret-here
NEXT_PUBLIC_APP_NAME=MyApp
NEXT_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
.env
.env.local
.env.development
.env.production
.env.staging
*.pem
*.key
credentials.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.

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

```
---
description: Environment configuration rules
globs: "*.config.{js,ts,mjs}, .env.*, **/config/**"
alwaysApply: false
---

- All configuration values MUST come from environment variables
- Provide sensible localhost defaults for development
- Use zod or joi to validate env vars at startup
- Never log environment variable values, only log variable names
- Pattern: `const config = { dbUrl: z.string().parse(process.env.DATABASE_URL) }`
- 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.

```
// Prompt to type in Cursor Composer (Cmd+I):
// @.env.example @.cursorrules
// Generate an env.ts module that:
// 1. Validates all env vars from .env.example are present at startup
// 2. Uses zod for type-safe validation
// 3. Exports a typed config object
// 4. Throws a clear error listing ALL missing vars, not just the first one
// 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 code example

File: `src/config/env.ts`

```typescript
import { z } from 'zod';

const serverSchema = z.object({
  DATABASE_URL: z.string().url(),
  REDIS_URL: z.string().url().optional(),
  JWT_SECRET: z.string().min(32),
  API_URL: z.string().url().default('http://localhost:3000'),
  NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'),
});

const clientSchema = z.object({
  NEXT_PUBLIC_APP_NAME: z.string().default('MyApp'),
  NEXT_PUBLIC_ANALYTICS_ID: z.string().optional(),
});

function validateEnv() {
  const serverResult = serverSchema.safeParse(process.env);
  const clientResult = clientSchema.safeParse(process.env);

  const errors: string[] = [];

  if (!serverResult.success) {
    errors.push(
      ...serverResult.error.issues.map(
        (issue) => `SERVER ${issue.path.join('.')}: ${issue.message}`
      )
    );
  }

  if (!clientResult.success) {
    errors.push(
      ...clientResult.error.issues.map(
        (issue) => `CLIENT ${issue.path.join('.')}: ${issue.message}`
      )
    );
  }

  if (errors.length > 0) {
    throw new Error(
      `Environment validation failed:\n${errors.join('\n')}`
    );
  }

  return {
    server: serverResult.data!,
    client: clientResult.data!,
  };
}

export const env = validateEnv();
export type ServerEnv = z.infer<typeof serverSchema>;
export type ClientEnv = z.infer<typeof clientSchema>;
```

## Common mistakes

- **Adding real .env files to Cursor's context with @file** — The contents of referenced files are sent to the AI model for processing. Real secrets in .env files would be transmitted to Cursor's servers. Fix: Only reference .env.example with placeholder values. Add real .env files to .cursorignore.
- **Forgetting to restart Cursor after changing .cursorrules** — Cursor caches rules at session start. Changes to .cursorrules may not take effect in existing chat sessions. Fix: Start a new chat session with Cmd+N after updating rules, or restart Cursor entirely.
- **Using NEXT_PUBLIC_ prefix for server-only secrets** — Variables prefixed with NEXT_PUBLIC_ or VITE_ are bundled into client-side JavaScript and visible to anyone viewing your site's source code. Fix: Add a rule to .cursorrules explicitly listing which prefixes are client-safe and which variables must remain server-only.
- **Not validating environment variables at startup** — Without startup validation, missing env vars cause cryptic runtime errors deep in your application instead of clear failure messages at boot. Fix: 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

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

---

Source: https://www.rapidevelopers.com/cursor-tutorial/how-to-specify-environment-variables-so-cursor-ai-s-suggestions-match-local-qa-or-production-setups
© RapidDev — https://www.rapidevelopers.com/cursor-tutorial/how-to-specify-environment-variables-so-cursor-ai-s-suggestions-match-local-qa-or-production-setups
