How to Use Environment Variables in Cypress v15.10.0
Set Cypress v15.10.0 values through config, files, OS variables, CLI flags, or setupNodeEvents. Use cy.env() for secrets and Cypress.expose() for public values.
In Cypress v15.10.0, set environment values in cypress.config.js or cypress.config.ts, cypress.env.json, operating-system variables, the CLI --env option, or setupNodeEvents. Read sensitive values explicitly with asynchronous cy.env(['KEY']). Read intentionally public values synchronously with Cypress.expose('key'). Cypress deprecated Cypress.env() in v15.10.0; it was removed in v16.0, so migrate accordingly. Cypress environment variables guide, cy.env() reference, Cypress.expose() reference.
1. Choose the right API for the value
| Value type | Use | Access | Visibility |
|---|---|---|---|
| Secret: token, password, API key | cy.env(['KEY']) |
Asynchronous Cypress command chain | Only requested keys are yielded, but the value is ordinary JavaScript once yielded |
| Public setting: feature flag, API version, environment label | expose configuration and Cypress.expose('key') |
Synchronous browser-context call | Public to application code, third-party scripts, and browser extensions |
Do not expose credentials with Cypress.expose(). Use a CI provider’s protected or masked secret facility for production credentials, then make the value available to the Cypress process. Cypress explains that Cypress.env() could hydrate every configured value into browser context, including values a test did not read. The newer APIs separate explicit secret access from intentionally public configuration.
2. Set and read a secret in a complete example
Read the secret from the operating-system environment when Cypress loads its config. The example assumes API_TOKEN has already been set in the shell or CI secret store.
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
config.env.apiToken = process.env.API_TOKEN
return config
},
},
})
// cypress/e2e/account.cy.js
describe('account API', () => {
it('loads the account with the configured token', () => {
cy.env(['apiToken']).then(({ apiToken }) => {
expect(Boolean(apiToken), 'API token is configured').to.equal(true)
cy.request({
url: '/api/account',
headers: { Authorization: `Bearer ${apiToken}` },
}).its('status').should('eq', 200)
})
})
})
The assertion checks only whether the token exists; it does not print the token. Keep the secret use inside the .then() callback and pass it directly to the operation that needs it. cy.env() logs requested key names, not values, but once the value is yielded, later assertions, .its(), .invoke(), failed chained commands, or console logging can expose it in logs.
3. Set public values with Cypress.expose()
In v15.10.0, put deliberately public browser-readable settings under the top-level expose configuration option. Read them synchronously from test code.
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
expose: {
apiVersion: 'v2',
featureCheckout: true,
testEnvironment: 'staging',
},
})
// cypress/e2e/checkout.cy.js
it('uses the public test settings', () => {
const apiVersion = Cypress.expose('apiVersion')
const checkoutEnabled = Cypress.expose('featureCheckout')
const environment = Cypress.expose('testEnvironment')
expect(apiVersion).to.equal('v2')
expect(checkoutEnabled).to.equal(true)
expect(environment).to.equal('staging')
})
Treat every exposed value as public. Browser application code and third-party scripts can access it. Do not put secrets in expose.
4. Choose where to define environment values
These sources supply values that tests can read with cy.env(). Key names are case-sensitive and must match exactly. Values can include strings, numbers, booleans, or objects depending on how they are configured.
Configuration file
Set values under the top-level env key. For credentials, load them from process.env instead of writing them into source control.
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
env: {
apiBase: 'https://staging.example.test',
apiToken: process.env.API_TOKEN,
retryCount: 2,
},
})
// cypress/e2e/config.cy.js
it('reads config values', () => {
cy.env(['apiBase', 'retryCount']).then(({ apiBase, retryCount }) => {
expect(apiBase).to.equal('https://staging.example.test')
expect(retryCount).to.equal(2)
})
})
cypress.env.json
Place this JSON file in the project root. Its values override conflicting keys in the Cypress config env block. If it contains sensitive values, add it to .gitignore and keep an example template without secrets for other developers.
{
"apiBase": "https://staging.example.test",
"apiToken": "replace-with-a-local-token"
}
Operating-system variables
Use the CYPRESS_ prefix for a custom test value. Cypress strips the prefix and normalizes the name. For example, provide CYPRESS_apiToken and read the corresponding normalized custom key from cy.env(). Avoid assuming punctuation or case normalization: use a simple key spelling consistently and verify the exact key in your configuration. CYPRESS_INTERNAL_ENV is reserved and must not be set.
# macOS or Linux shell
export CYPRESS_apiToken='replace-with-a-local-token'
npx cypress run
# PowerShell
$env:CYPRESS_apiToken = 'replace-with-a-local-token'
npx cypress run
The CYPRESS_ prefix also overrides Cypress configuration settings. For example, CYPRESS_BASE_URL changes baseUrl; this is different from defining a custom value for tests.
CLI --env
Pass comma-separated key=value entries for local, non-secret overrides:
npx cypress run --env host=staging.example,region=west
For values containing commas or nested objects, provide JSON as a string and quote it according to the shell in use. Be careful with shell expansion and quoting differences between Bash, PowerShell, and CI runners. Do not pass production secrets in a command line: command history or CI logs may reveal them. Use the CI provider’s secret store and OS environment instead.
setupNodeEvents
The Node-side setup hook can set values dynamically, such as reading a secret from the process environment. Return the updated config so Cypress can use it:
setupNodeEvents(on, config) {
config.env.apiToken = process.env.API_TOKEN
config.env.region = process.env.TEST_REGION || 'us-east'
return config
}
5. Keep Cypress configuration separate from test values
Some prefixed OS variables override Cypress settings rather than creating custom test environment values. Examples include CYPRESS_BASE_URL, CYPRESS_REPORTER, and viewport settings. Use these to configure the Cypress runner, and use the custom environment mechanisms above for values your tests need.
For Cypress Cloud recording, CYPRESS_RECORD_KEY and CYPRESS_PROJECT_ID are read from the operating-system environment. Cypress’s CI guide says they cannot be supplied through cypress.env.json or the config env block for recording. Configure them as protected CI variables and ensure the Cypress process receives them. See the Cypress CI guide and CLI reference.
6. Migrate from Cypress.env() in v15.10.0
- Find every
Cypress.env()call and identify whether each value is secret or public. - Replace secret reads with
cy.env(['key']), accounting for the asynchronous command chain. - Move public browser-readable values to
exposeand read them withCypress.expose('key'). - Check plugins, support files, and CLI scripts for assumptions about the old synchronous API.
- After migrating, set
allowCypressEnv: falsein v15.10.0 to make remaining old API uses fail visibly.
allowCypressEnv is specific to the v15.10.0 transition. Remove it when upgrading to Cypress 16.0, where both it and Cypress.env() were removed. Do not treat v16 behavior as if it applied to the 15.10.0 migration period. See the Cypress migration guide and Cypress.env() reference.
7. Troubleshoot common problems
| Symptom | Likely cause | Fix |
|---|---|---|
cy.env is unavailable |
The installed Cypress version predates v15.10.0, or the project is using a different binary than expected. | Check the installed version and use the v15.10.0 API only when that version is running. |
Cypress.env() throws or is absent |
The project is on Cypress 16.0, where it was removed. | Migrate secret reads to cy.env() and public reads to Cypress.expose(). |
cy.env(['apiToken']) returns an undefined value |
The key was not configured, the config hook did not copy it, the OS variable is missing, or the spelling/case differs. | Check the secret is present in the Cypress process environment, confirm the config is returned, and match the key exactly. |
| A JSON or CLI value is unexpectedly a string or malformed | Shell quoting or JSON serialization changed the value, or a delimiter split the CLI list. | Use valid JSON, quote for the active shell, and prefer a config file or setup hook for structured values. |
Config value differs from cypress.env.json |
The JSON file overrides a conflicting config env entry. |
Remove the duplicate or set the intended value in the higher-priority source. |
| Secret appears in a log | The value was asserted, inspected, or logged after cy.env() yielded it. |
Keep it inside a callback, avoid logging it, and assert only a derived boolean or status. |
| Cypress Cloud recording cannot find its key | The key was put in Cypress config or cypress.env.json instead of the OS environment. |
Set CYPRESS_RECORD_KEY and CYPRESS_PROJECT_ID as CI secrets available to the Cypress process. |
| A prefixed variable changes runner behavior instead of appearing as a test value | The name matches a Cypress configuration override such as CYPRESS_BASE_URL. |
Use a distinct custom key for test data and reserve known config names for Cypress settings. |
8. Reliability, performance, and cost notes
- Reliability: Keep per-environment values in one deliberate source where practical. If the same key appears in several sources, know which source wins; in particular,
cypress.env.jsonoverrides conflicting configenvvalues. - Security: Prefer CI secret storage and OS environment injection for credentials. Avoid committing local secret files or putting secrets in command-line arguments.
- Performance: Environment lookup is configuration access, not a substitute for Cypress waiting or synchronization.
cy.env()is asynchronous because it is a Cypress command; chain it and use the result in the callback. - Cost: Cypress environment configuration itself has no per-read cost described by the cited documentation. CI runtime and any external services your tests call can have their own costs; manage those separately.
9. Or skip the browser setup
If the task is capturing a page for a visual check, screenshot, or PDF, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It accepts one GET request with a URL and returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation.
# cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
# Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
// Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners are accepted and removed before capture; more than 60 known consent platforms, newsletter popups, and chat widgets can be removed, and each step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify the page verdict and billing status in headers.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The Free plan includes 1,000 screenshots monthly with no card. Paid plans start at $5 for 3,000 screenshots; yearly billing gives two months free, and every feature is on every plan.
Sign up free for 1,000 screenshots a month, with no card.
10. FAQ
Can I use Cypress environment variables to set the browser’s process.env?
No. The operating-system environment is available to Node-side configuration; browser tests should receive values through Cypress’s supported configuration APIs.
Does cy.env() change or set a value?
No. It reads configured values. Set values through configuration, files, OS variables, CLI options, or the Node setup hook.
Should every test value use cy.env()?
No. Use it for sensitive values. For intentionally public browser settings, use Cypress.expose().
Can I leave allowCypressEnv enabled after upgrading?
No. It is a v15.10.0 migration option and was removed in v16.0.


