env
Securely read environment variables set in your Cypress configuration, cypress.env.json, or CYPRESS_* environment variables from within Cypress tests.
Read-Only Command
cy.env() is read-only and can only retrieve environment variables. It cannot set or modify environment variables. To set environment variables, use one of the supported methods described in the Environment Variables & Secrets guide.
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(),
.invoke(), and any chained command that fails can all
print a value to the Command Log and
the console output. What happens
to a value after cy.env() yields it is up to you. See
Handling the yielded value safely.
Syntax
cy.env(keys)
cy.env(keys, options)
Usage
Correct Usage
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
env: {
apiUrl: 'https://api.example.com',
apiKey: 'secret-key-12345',
},
expose: {
environment: 'staging', // Public configuration value
},
})
import { defineConfig } from 'cypress'
export default defineConfig({
env: {
apiUrl: 'https://api.example.com',
apiKey: 'secret-key-12345',
},
expose: {
environment: 'staging', // Public configuration value
},
})
// Get a single environment variable
cy.env(['apiUrl']).then(({ apiUrl }) => {
cy.request(`${apiUrl}/users`).its('status').should('eq', 200)
})
// Get multiple environment variables
cy.env(['apiUrl', 'apiKey']).then(({ apiUrl, apiKey }) => {
cy.request({
url: `${apiUrl}/users`,
headers: { Authorization: `Bearer ${apiKey}` },
})
.its('status')
.should('eq', 200)
})
// With options
cy.env(['apiUrl'], { log: false }).then(({ apiUrl }) => {
// Use apiUrl
})
Arguments
keys (String[])
An array of environment variable keys to retrieve from Cypress. These environment variables can be set via any of the methods described in the Environment Variables & Secrets guide.
Environment variables cannot be set using test configuration.
If a variable is not defined, its value will return undefined in the returned object.
options (Object)
Pass an options object to change the default behavior of cy.env().
| Option | Default | Description |
|---|---|---|
log | true | Displays the command in the Command log. Only variable names are logged, never values. |
timeout | 4000 | Time to wait for cy.env() to resolve before timing out. |
Yields
cy.env() yields an object with the values found for the environment variable keys requested.
cy.env(['apiUrl', 'apiKey']).then((env) => {
// env = { apiUrl: 'https://api.example.com', apiKey: 'secret-key-12345' }
})
Examples
Single Variable
Get a single environment variable:
cy.env(['apiUrl']).then(({ apiUrl }) => {
cy.visit(apiUrl)
})
Multiple Variables
Get multiple environment variables at once:
cy.env(['apiUrl', 'apiKey', 'timeout']).then(({ apiUrl, apiKey, timeout }) => {
cy.request({
url: `${apiUrl}/users`,
headers: { Authorization: `Bearer ${apiKey}` },
timeout: timeout || 5000,
})
})
Use environment variables across tests
Store environment variables for use across multiple tests by using a before() hook:
describe('API tests', () => {
let apiBaseUrl
before(() => {
cy.env(['apiBaseUrl']).then(({ apiBaseUrl: url }) => {
apiBaseUrl = url
})
})
it('can make requests', () => {
cy.request(`${apiBaseUrl}/users`).its('status').should('eq', 200)
})
})
Handle required vs optional variables
Variables that are not set will be undefined:
cy.env(['requiredVar', 'optionalVar']).then(({ requiredVar, optionalVar }) => {
if (requiredVar === undefined) {
throw new Error('requiredVar must be set in Cypress configuration')
}
// Use optionalVar only if it's defined
if (optionalVar) {
// Use optionalVar
}
})
Chaining with other commands
cy.env(['baseUrl']).then(({ baseUrl }) => {
cy.visit(baseUrl)
cy.get('h1').should('be.visible')
})
Use cy.env in custom commands
Create custom commands that use cy.env():
- cypress/support/commands.js
- cypress/support/commands.ts
// cypress/support/commands.js
Cypress.Commands.add('apiRequest', (endpoint, options = {}) => {
cy.env(['apiUrl', 'apiKey']).then(({ apiUrl, apiKey }) => {
cy.request({
url: `${apiUrl}${endpoint}`,
headers: {
Authorization: `Bearer ${apiKey}`,
...options.headers,
},
...options,
})
})
})
// In your test
cy.apiRequest('/users').its('status').should('eq', 200)
declare global {
namespace Cypress {
interface Chainable {
apiRequest(
endpoint: string,
options?: Partial<Cypress.RequestOptions>
): Chainable<Cypress.Response<any>>
}
}
}
// cypress/support/commands.js
Cypress.Commands.add('apiRequest', (endpoint, options = {}) => {
cy.env(['apiUrl', 'apiKey']).then(({ apiUrl, apiKey }) => {
cy.request({
url: `${apiUrl}${endpoint}`,
headers: {
Authorization: `Bearer ${apiKey}`,
...options.headers,
},
...options,
})
})
})
// In your test
cy.apiRequest('/users').its('status').should('eq', 200)
Suppress command logging
Hide the cy.env() entry from the Command Log. Because cy.env() logs key
names and never values, use this option when you don't want the key names
visible in the Command Log:
cy.env(['acmeMigrationKey'], { log: false }).then(({ acmeMigrationKey }) => {
// The command and the key name don't appear in the Command Log
})
Handling the yielded value safely
cy.env() logs the key names you ask for and never the values. That guarantee
stops the moment the command yields. The object you receive is an ordinary
JavaScript object, and Cypress does not mask, redact, or track the values
inside it.
Keep the value inside a .then() callback and pass it straight to the command
that needs it. .then() and
.spread() add no entry to the Command Log, so the
value stays out of it.
Assert on a derived value, not on the secret
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
Calling expect() inside a .then() callback still creates an assertion entry,
so a .then() callback alone is not enough. What you assert on is what matters.
Avoid .its() and .invoke() on the yielded object
.its() and .invoke() both add
the subject they were applied to and the value they yield to the console
output, so cy.env(['apiKey']).its('apiKey') prints the environment variable
value twice. Read the property inside a .then() callback instead.
Incorrect Usage
// ❌ Prints the environment variable value to the console output twice
cy.env(['apiKey']).its('apiKey')
Correct Usage
cy.env(['apiKey']).then(({ apiKey }) => {
// ✅ Use apiKey here
})
Keep the value out of downstream command logs
Commands that accept a log option, such as
cy.request() and .type(),
leave their entry out of the Command Log when you pass { log: false }. Use it
on any command you hand the value to:
cy.env(['apiKey']).then(({ apiKey }) => {
cy.request({
url: 'https://api.example.com/users',
headers: { Authorization: `Bearer ${apiKey}` },
log: false,
})
cy.get('[data-testid="token-field"]').type(apiKey, { log: false })
})
{ log: false } hides the entry from the Command Log. It does not redact the
value, and it has no effect on assertions.
Why use cy.env()?
cy.env() was introduced to replace Cypress.env(), which was deprecated in Cypress 15.10.0 and removed in Cypress 16.0.
Secure access to sensitive values
Unlike Cypress.env() (removed in Cypress 16.0), which hydrated all environment variables into the browser:
cy.env()only exposes the variables you explicitly request- Variables are not automatically serialized into browser state
- Only requested variables are passed into
cy.origin()contexts - Sensitive data is easier to audit and control
This reduces accidental exposure and limits the blast radius of secrets.
When to use cy.env() vs Cypress.expose()
Both cy.env() and Cypress.expose() provide access to configuration values in Cypress, but they serve different security and execution needs. Choosing the right API helps avoid accidental exposure of sensitive data and keeps configuration intent clear.
Use Cypress.expose() for public configuration
Recommended when:
- Values are public or non-sensitive - Examples include feature flags, API versions, environment labels, or plugin configuration that is safe to appear in browser state.
- Synchronous access is needed -
Cypress.expose()returns values immediately, without requiring Cypress command chaining.
Use cy.env() for sensitive or secret values
Choose cy.env() when security, scoping, and controlled access matter. Recommended when:
- Values are sensitive - API keys, passwords, tokens, or any data that should not be broadly exposed to the browser.
- Security is a priority -
cy.env()only exposes the variables you explicitly request and does not automatically serialize them into browser state. - You're already working within Cypress command chains:
cy.env()is asynchronous and designed to be used inside Cypress tests and hooks.
Example: choosing the right API
// ✅ Use cy.env() for sensitive values
cy.env(['apiKey']).then(({ apiKey }) => {
cy.request({
url: 'https://api.example.com/users',
headers: { Authorization: `Bearer ${apiKey}` },
})
})
// ✅ Use Cypress.expose() for public configuration
const apiVersion = Cypress.expose('apiVersion') // Synchronous, public value
const featureFlag = Cypress.expose('featureFlag') // Safe to expose in browser
See Cypress.expose() for more details.
Migrate from Cypress.env()
cy.env() replaces the removed Cypress.env() API, which was deprecated in Cypress 15.10.0 and removed in Cypress 16.0.
On Cypress ^15.10.0, after migrating all usages, you can prevent future use of Cypress.env() by setting allowCypressEnv: false in your Cypress configuration. In Cypress 16.0, allowCypressEnv has been removed — Cypress.env() is no longer available regardless of configuration.
Cypress ^15.10.0 only:
- cypress.config.js
- cypress.config.ts
const { defineConfig } = require('cypress')
module.exports = defineConfig({
allowCypressEnv: false,
})
import { defineConfig } from 'cypress'
export default defineConfig({
allowCypressEnv: false,
})
See the Migration Guide for detailed migration instructions.
Notes
Read-only behavior
cy.env() is read-only and cannot set environment variables at runtime.
See the Environment Variables & Secrets guide for more details on setting environment variables.
Test configuration overrides
Environment variables cannot be set using test configuration. On Cypress ^15.10.0, this was enforced when allowCypressEnv was set to false. In Cypress 16.0, env in test configuration overrides has been removed entirely — if absolutely necessary, expose can be used instead.
Case sensitivity
Variable names are case-sensitive and must match exactly how they are defined in your configuration:
// Configuration
{
env: {
apiUrl: 'https://api.example.com',
}
}
// In test
cy.env(['apiUrl']) // ✅ Gets 'https://api.example.com'
cy.env(['APIURL']) // ❌ Returns undefined
History
| Version | Changes |
|---|---|
| 15.10.0 | cy.env() command added |