12k
All articles

What's New in Cypress 16

Explore Cypress 16 changes, including native HTTP/2, faster test defaults, removed APIs and migration fixes. See why to upgrade to Cypress 16.1.0 or later.

OpenReplay Team
OpenReplay Team
What's New in Cypress 16

Cypress 16 runs Chrome, Chromium and Edge on the browser’s native network stack with HTTP/2. It also types and checks visibility faster by default and removes Cypress.env() and cy.exec(). Upgrade to 16.1.0 or later, not 16.0.0.

Major upgrades usually break in CI, not locally. A removed API throws inside a support file, or a run stops printing output until the job times out.

This article covers Cypress 16.0.0 (September 1, 2026) and 16.1.0 (September 15, 2026): what gets faster, what changes in how you write tests, what breaks and how to fix it, and which version to pin. For the fundamentals, see end-to-end testing with Cypress.

Key Takeaways

  • In Cypress 16, Chrome, Chromium and Edge use HTTP/2 or HTTP/3 whenever the server supports it. Firefox, WebKit and Electron stay on HTTP/1.1.
  • Cypress.env() is removed. Use cy.env() for secrets, which stay in the Node process until a test asks for them, and Cypress.expose() for values that are safe in the browser.
  • cy.intercept() has the same API, but on the native network req.httpVersion is missing, compression headers are gone, and revalidated responses report 200 instead of 304.
  • Cypress 16 requires Node.js 22.x, 24.x or >=26.x. Node 20 and 25 are no longer supported.
  • Use 16.1.0 or later. 16.1.0 fixes a 16.0.0 regression where Chrome-family runs could hang with no output, and another where blockHosts was not enforced.

HTTP/2 Is the Default in Chrome, Chromium and Edge

Chrome, Chromium and Edge now intercept Cypress 16 test traffic on the browser’s own network stack instead of passing it through the Cypress proxy. The app under test connects straight to your server and uses the best protocol both sides support, HTTP/2 or HTTP/3 included. Firefox, WebKit and Electron keep the legacy HTTP/1.1 path.

The switch to native HTTP/2 matters because HTTP/1.1 allows only a few parallel connections per origin, so a page with hundreds of assets has to queue them. HTTP/2 multiplexes all of those requests as streams over a single connection. Cypress’s own benchmark of a page loading 1,000 images dropped from 3,896ms over HTTP/1.1 to 1,362ms over HTTP/2. The announcement doesn’t describe the test setup, so read the figure as a direction rather than a prediction for your suite.

Native HTTP/2 also makes streaming UIs testable in Cypress 16. Over HTTP/1.1, a browser allows only six Server-Sent Events connections per domain, which put features like live notifications out of reach. Over HTTP/2, the server and browser agree on how many streams can run at once, and Cypress puts the usual default at 100.

Cypress 16 also no longer terminates TLS for the app under test, so the browser checks your origin’s real certificate. For self-signed development certificates or an internal CA, 16.1.0 adds trustedCertificates.

Test Speed Changes in Cypress 16

Cypress 16 changes three defaults that affect speed. Each one has a way back.

  • Keystroke delay is 0ms, down from 10ms. For inputs that depend on typing pace, such as debounced search fields, you can restore the old delay:
Cypress.Keyboard.defaults({ keystrokeDelay: 10 })
cy.get('[data-cy=search]').type('laptop', { delay: 10 })

On 16.0.0, putting keystrokeDelay in a TypeScript config file failed to compile. 16.1.0 fixes that.

  • Visibility checks use the modern algorithm. It is built on the browser’s native Element.checkVisibility(), and visibilityStrategy defaults to 'modern'.
  • manageBrowserMemory is on by default. While tests run, Cypress checks memory use at intervals. If a reading goes over a set limit, it runs garbage collection before the next test starts. That cleanup costs time, so a light suite might be quicker with the setting off. Time your suite both ways before you turn it off.

What Changes in How You Write Cypress 16 Tests?

In Cypress 16, cookie and storage reads retry like other queries, and environment access is split into two separate APIs.

cy.getCookie(), cy.getCookies(), cy.getAllCookies(), cy.getAllLocalStorage() and cy.getAllSessionStorage() are now retrying query commands governed by defaultCommandTimeout. They no longer use responseTimeout, although cy.setCookie() still does. Chain a .should() and it keeps retrying until it passes. Put the check inside .then() and it runs once, with no retry.

cy.getCookie('auth_token').should('have.property', 'value')

If your suite overrides any of these commands, switch to Cypress.Commands.overwriteQuery(). Cypress.Commands.overwrite() no longer works on them.

Cypress.env() was deprecated in 15.10.0 and is now gone. cy.env() is asynchronous: it pulls sensitive values from the Node process and hands your test only the keys you name. Cypress.expose() is the synchronous option for values that are safe to show in the browser.

// Before (Cypress 15)
const token = Cypress.env('STRIPE_KEY')

// After (Cypress 16)
cy.env(['STRIPE_KEY']).then(({ STRIPE_KEY }) => {
  cy.request({ url: '/api/charge', headers: { Authorization: STRIPE_KEY } })
})
const apiBase = Cypress.expose('PUBLIC_API_URL')

cy.env() writes key names to the Command Log but never the values. That protection stops once it hands you the object: .its(), .invoke(), assertions or a failing command can all print a value, so keep secrets inside .then(). Per-test and suite overrides now take expose: {...} instead of env: {...}, and allowCypressEnv has been removed.

Cypress 16 Breaking Changes and Fixes

Most Cypress 16 breaks are mechanical removals, and the migration guide has a direct replacement for each. The one that needs thought is cy.intercept().

The cy.intercept() API itself is unchanged. On the native network, though, req.httpVersion is not reported, compression headers such as content-encoding are missing because the body arrives already decoded, revalidated responses report 200 instead of 304, and res.statusMessage is empty over HTTP/2 and HTTP/3. responseTimeout also no longer limits response handlers. If no response arrives, Cypress gives up after a fixed 30 seconds, and if you need a different limit you set it on cy.wait(). Change assertions so they target behaviour, not transport details. The patterns in mocking and stubbing in Cypress still apply.

// Before: expect(req.httpVersion).to.eq('1.1')
cy.intercept('GET', '/api/cart').as('cart')
cy.visit('/checkout')
cy.wait('@cart', { timeout: 15000 }).its('response.statusCode').should('eq', 200)

The full list of 16.0.0 breaking changes, with what happens if you leave each one in place:

ChangeIf unfixedFix
cy.exec() / execTimeout removedcy.exec() throws; execTimeout warns and is ignoredMove the logic into a cy.task() and use taskTimeout
cy.end() removedCommand errorsDelete the calls
viewportWidth/viewportHeight/blockHosts via Cypress.config()No longer allowed at runtime (previously it applied to the next test)Set them in describe/it config, or call cy.viewport()
experimentalMemoryManagementWarns, ignoredDelete it; use manageBrowserMemory: false to opt out
experimentalFastVisibilityReplacedDelete it; visibilityStrategy controls this now
experimentalSourceRewriting removedOption is deadDelete it; for SRI errors use removeSRIAttributes
CoffeeScript.coffee files no longer resolveUse JS/TS, or @cypress/webpack-preprocessor
Node.jsNode 20 and 25 unsupportedNode 22.x, 24.x or >=26.x
Component testingOlder toolchains unsupportedAngular 21.0.0+, Vite 8.0.0+, Next.js 15.0.4+ or 16+
ElectronDeprecated, still runsSwitch CI to Chrome or another installed browser

For viewport, move the setting into block config:

describe('mobile nav', { viewportWidth: 375, viewportHeight: 667 }, () => {
  it('opens the menu', () => { /* ... */ })
})

Cypress 16 Escape Hatches Are Temporary

Cypress 16 ships two opt-outs, and the changelog marks both as deprecated. forceHttp1: true sends every browser back through the legacy proxy path. Cypress marked it deprecated in the same release that added it, and plans to drop it once the native path has long-term support in both Chrome and Firefox. visibilityStrategy: 'legacy' restores the old visibility algorithm. The option itself is deprecated, and the changelog says to treat the legacy value as a stopgap while you migrate.

Use forceHttp1 and visibilityStrategy: 'legacy' to diagnose failures, not as permanent settings. If a failure goes away with the flag on, compare it against the documented native-network differences, rewrite the assertion, and then remove the flag.

Verdict: Upgrade to Cypress 16.1.0 or Later, Not 16.0.0

Upgrade, and pin 16.1.0 or a newer 16.x release, never 16.0.0. 16.1.0 fixes a 16.0.0 regression where runs in Chrome, Chromium and Edge could go silent partway through and never finish. It also fixes blockHosts not being enforced in those browsers: requests to a blocked host went out to the network when they should have failed with a 503. A suite that depends on blocked hosts could pass on 16.0.0 without actually testing what it claims. 16.1.0 also fixes the keystrokeDelay TypeScript error and a service-worker memory leak.

Run your first green build on 16 in Chrome, not only Electron. Electron stays on the legacy path, so an Electron-only run never touches the native network. After that, check your suite against Cypress best practices. Your Firefox and WebKit runs still use HTTP/1.1, while real Firefox and Safari users get HTTP/2 in production, so session replay of real users is a useful way to find the flows and browsers your E2E suite never exercises.

Conclusion

Most of what Cypress 16 speeds up needs no work from you. Most of what it breaks can be found by searching your repo. Search for Cypress.env, cy.exec, cy.end, runtime Cypress.config() calls, and the removed experimental keys. Replace each hit using the table above, move CI to Node 22 or later with Chrome, and install 16.1.0 or later directly.

FAQs

How do I run a shell command in Cypress 16 now that cy.exec() is removed?

Register a task in setupNodeEvents that calls Node's child_process module, for example execFileSync, then call it from the test with cy.task('resetDatabase'). Every task has to return a value or null. By default a task gives up after 60 seconds, and taskTimeout changes that limit. cy.task() cannot run processes that never exit, such as starting a server. cy.exec() was deprecated in Cypress 15.21.0 and removed in 16.0.0.

What is the difference between trustedCertificates and clientCertificates in Cypress?

trustedCertificates, added in Cypress 16.1.0, makes the browser trust a certificate your server presents, such as a self-signed or internal-CA development certificate. clientCertificates is for mutual TLS, where Cypress presents a certificate to the server. Each trustedCertificates entry takes exactly one of filePath, pem or spki, and it applies on the native network path in Chrome, Chromium and Edge. Declared origins can then use the browser's HTTP cache.

Can I run Angular 20 component tests on Cypress 16?

Not with the built-in harness. Cypress 16 component testing no longer supports Angular 18, 19 or 20, and the minimum supported version is Angular 21.0.0. As a stopgap, you can install the deprecated @cypress/angular 4.x package from npm and import mount from it instead of cypress/angular, but Cypress no longer supports that harness, so plan the move to Angular 21. The same release also removes built-in CoffeeScript support, so .coffee specs, support files and fixtures no longer resolve.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.