---
id: app/configure/environment-variables
title: Environment variables and secrets in Cypress
description: >-
  Learn how to manage environment variables and secrets in Cypress. Understand
  when to use cy.env(), when to use Cypress.expose(), and how to safely pass
  values across environments.
section: app
source_path: docs/app/configure/environment-variables.mdx
version: 245b50e4ce1d3686f3ab1bd3a5e1e6e8edaa94b2
updated_at: '2026-09-17T18:14:18.180Z'
---
# Environment Variables & Secrets

Cypress tests often need values that change across environments like API URLs, credentials, feature flags, or configuration toggles.

Not all values should be treated the same. Some are **secrets that must be protected**, while others are **safe to expose** and convenient to access synchronously.

This guide helps you choose the right API for your use case.

## Secrets and sensitive values

**Use [`cy.env()`](/llm/markdown/api/commands/env.md)** for sensitive values like API keys, passwords, tokens, or credentials.

`cy.env()` retrieves only the values you explicitly request, when you need them, and avoids exposing all environment variables in browser state. This provides privileged access that doesn't automatically serialize values into browser context.

**Examples of secrets:**

*   API keys
*   Authentication tokens
*   Passwords
*   Database credentials
*   Private service endpoints

```
cy.env(['apiKey']).then(({ apiKey }) => {
  cy.request({
    url: 'https://api.example.com/data',
    headers: { Authorization: `Bearer ${apiKey}` },
  })
})
```

See the [`cy.env()`](/llm/markdown/api/commands/env.md) command documentation for complete details.

`cy.env()` logs the key names you ask for and never the values. That protection ends at the command boundary. The object `cy.env()` yields is an ordinary JavaScript object, and Cypress does not mask, redact, or track the values inside it. Assertions, [`.its()`](/llm/markdown/api/commands/its.md), [`.invoke()`](/llm/markdown/api/commands/invoke.md), and any chained command that fails can all print a value to the [Command Log](/llm/markdown/app/core-concepts/open-mode.md#Command-Log) and the [console output](/llm/markdown/app/core-concepts/open-mode.md#Console-output). What happens to a value after `cy.env()` yields it is up to you. See [Handling the yielded value safely](/llm/markdown/api/commands/env.md#Handling-the-yielded-value-safely).

### Handling the yielded value safely

Keep the value inside a `.then()` callback and pass it straight to the command that needs it. [`.then()`](/llm/markdown/api/commands/then.md) adds no entry to the Command Log.

Every assertion writes to the Command Log, and the entry contains the values being compared. Assertions accept no logging options, so you cannot suppress them. Assert on a boolean you derive from the value instead.

**Incorrect Usage**

```
cy.env(['apiKey']).should('deep.include', { apiKey: 'secret-key-12345' })
// ❌ Command Log: assert expected { apiKey: 'secret-key-12345' } to deep
// include { apiKey: 'secret-key-12345' }
```

**Correct Usage**

```
cy.env(['apiKey']).then(({ apiKey }) => {
  expect(Boolean(apiKey)).to.be.true
})
// ✅ Command Log: assert expected true to be true
```

## Public configuration values

**Use [`Cypress.expose()`](/llm/markdown/api/cypress-api/expose.md)** for public, non-sensitive configuration values.

`Cypress.expose()` provides synchronous access to configuration that is safe to expose in the browser context. Values are accessible to application code, third-party scripts, and browser extensions.

**Examples of public configuration:**

*   Feature flags
*   API versions
*   Plugin configuration
*   Environment labels (staging, prod)
*   Public service URLs

```
const apiVersion = Cypress.expose('apiVersion') // Synchronous access
const featureFlag = Cypress.expose('featureFlag')

if (featureFlag) {
  cy.get(`[data-testid="feature-${apiVersion}"]`).should('be.visible')
}
```

See the [`Cypress.expose()`](/llm/markdown/api/cypress-api/expose.md) API documentation for complete details.

## Set environment variables

Environment variables for `cy.env()` can be set using several methods:

1.  **Cypress configuration file** - Set in the `env` key of your Cypress configuration
2.  **`cypress.env.json` file** - Create a `cypress.env.json` file in your project root
3.  **`CYPRESS_*` environment variables** - Set OS-level environment variables with `CYPRESS_` prefix
4.  **`--env` CLI flag** - Pass environment variables via command line
5.  **`setupNodeEvents`** - Set dynamically in the `setupNodeEvents` function

### 1\. Configuration File

Set environment variables in your Cypress configuration file under the `env` key:

*   cypress.config.js
*   cypress.config.ts

```
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  env: {
    apiUrl: 'https://api.example.com',
    apiKey: process.env.API_KEY, // From OS environment
  },
})
```

```
import { defineConfig } from 'cypress'

export default defineConfig({
  env: {
    apiUrl: 'https://api.example.com',
    apiKey: process.env.API_KEY, // From OS environment
  },
})
```

### 2\. `cypress.env.json`

Create a `cypress.env.json` file in your project root. Values here override conflicting environment variables in your Cypress configuration.

```
{
  "host": "veronica.dev.local",
  "api_server": "http://localhost:8888/api/v1/"
}
```

**Important**: Add `cypress.env.json` to `.gitignore` if it contains sensitive data.

### 3\. `CYPRESS_*` environment variables

Set OS-level environment variables with the `CYPRESS_` or `cypress_` prefix:

```
export CYPRESS_HOST=laura.dev.local
export cypress_api_server=http://localhost:8888/api/v1/
```

Cypress automatically removes the leading `CYPRESS_` or `cypress_` prefix and normalizes the name.

The environment variable `CYPRESS_INTERNAL_ENV` is reserved and should not be set.

Some `CYPRESS_`\-prefixed variables are consumed by the Cypress CLI and Cypress Cloud rather than being treated as test environment variables. Most notably, [`CYPRESS_RECORD_KEY`](/llm/markdown/app/references/command-line.md#cypress-run-record-key-lt-record-key-gt) and [`CYPRESS_PROJECT_ID`](/llm/markdown/cloud/account-management/projects.md#Project-ID) are read directly from your operating system environment when recording to Cypress Cloud. They must be set as OS-level environment variables (as shown above) and **cannot** be supplied through `cypress.env.json` or the `env` block of your Cypress configuration.

### 4\. `--env` CLI flag

Pass environment variables via the command line. Multiple values must be separated by a comma, not a space. In some shells, like Windows PowerShell, you may need to surround the key/value pair with quotes.

```
cypress run --env host=kevin.dev.local,api_server=http://localhost:8888/api/v1
```

To pass a complex value, such as an object with nested fields, provide it as a JSON string. This is also the way to include values that contain commas, spaces, or quotes, since those characters would otherwise be interpreted by the shell or as delimiters.

```
cypress run --env credentials='{"apiKey":"secret-key-12345","auth":{"user":"jane","token":"abc123"}}'
```

See the [`--env`](/llm/markdown/app/references/command-line.md#cypress-run-env-lt-env-gt) command line reference for more details.

### 5\. setupNodeEvents

Set environment variables dynamically in the `setupNodeEvents` function:

*   cypress.config.js
*   cypress.config.ts

```
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      export default defineConfig({
        e2e: {
          setupNodeEvents(on, config) {
            config.env.apiKey = process.env.API_KEY
            return config
          },
        },
      })
    },
  },
})
```

```
import { defineConfig } from 'cypress'

export default defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      export default defineConfig({
        e2e: {
          setupNodeEvents(on, config) {
            config.env.apiKey = process.env.API_KEY
            return config
          },
        },
      })
    },
  },
})
```

## Set exposed configuration

Exposed configuration for `Cypress.expose()` can be set via:

1.  **Cypress configuration file** - Set in the `expose` key of your Cypress configuration
2.  **`--expose` CLI flag** - Pass exposed configuration via command line

### 1\. Configuration file

Set exposed configuration in your Cypress configuration file under the `expose` key:

*   cypress.config.js
*   cypress.config.ts

```
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  expose: {
    apiVersion: 'v2',
    featureFlag: true,
    environment: 'staging',
  },
})
```

```
import { defineConfig } from 'cypress'

export default defineConfig({
  expose: {
    apiVersion: 'v2',
    featureFlag: true,
    environment: 'staging',
  },
})
```

### 2\. `--expose` CLI flag

Pass exposed configuration via the command line:

```
cypress run --expose apiVersion=v2,featureFlag=true
```

See the [`Cypress.expose()`](/llm/markdown/api/cypress-api/expose.md) API documentation for complete details on setting exposed configuration.

## Migrate from Cypress.env()

`Cypress.env()` was removed in Cypress 16.0. If your tests still call it, migrate each value to the appropriate modern API:

*   **Sensitive values** → [`cy.env()`](/llm/markdown/api/commands/env.md)
*   **Public configuration** → [`Cypress.expose()`](/llm/markdown/api/cypress-api/expose.md)

See the [Migration Guide](/llm/markdown/app/references/migration-guide.md#Migrating-away-from-Cypressenv) for detailed migration instructions.

## See also

*   [`cy.env()`](/llm/markdown/api/commands/env.md)
*   [`Cypress.expose()`](/llm/markdown/api/cypress-api/expose.md)
*   [Configuration](/llm/markdown/app/references/configuration.md)
*   [Best Practices: Handling Secrets](/llm/markdown/app/core-concepts/best-practices.md#Handling-Secrets-and-Sensitive-Data)
