Cypress 16: faster tests, starting with HTTP/2 support

September 1, 2026

By Jennifer Shehane

Your fastest test runs yet. Meet Cypress 16.

Slow end-to-end test suites rarely have one cause. Time leaks out through queued requests, artificial delays, layout thrashing, and flaky reruns that force a full CI re-trigger. Cypress 16 goes after all of them at once, and most of it arrives without you editing a single test:

  • HTTP/2 support, on by default in Chromium-based browsers (excluding Electron), so request-heavy pages load in parallel instead of queuing
  • Typing at full speed, with the default delay between keystrokes now zero
  • A faster visibility algorithm, which matters most on large, deeply nested applications
  • Cookie and storage commands that retry, removing a common source of flake and the reruns that follow it
  • Memory management on by default, so long runs stop degrading and crashing partway through

The release also modernizes the foundation Cypress runs on, moving to Node.js 24, Electron 41, and Chromium 146, and it completes the security work started in Cypress 15 by removing Cypress.env() so secrets stay out of the browser.

Our own test performance guide puts "upgrade to the latest Cypress" at the top of its priority checklist, ahead of every configuration change you could make. Cypress 16 is a good argument for why.

HTTP/2 support

Historically, Cypress sits between the browser and your servers, proxying every request so commands like cy.intercept() can observe and modify network traffic. When we built that proxy, HTTP/1.1 was the standard. The web moved on, and today the overwhelming majority of production traffic is HTTP/2. Until now, your app ran over an older protocol under test than it does for real users.

In Cypress 16, requests use HTTP/2 whenever the server supports it, in Chromium-based browsers, with no changes to your tests.

Request-heavy pages get faster. HTTP/2 multiplexes many requests over one connection instead of queuing them behind a small number of parallel slots. In our benchmark loading 1,000 images, the HTTP/2 page finished in 1,362ms against 3,896ms over HTTP/1.1. If your pages fire off many small requests, especially through a Vite dev server, you should feel the difference in open mode and in CI.

Streaming features become testable. Over HTTP/1.1, browsers cap Server-Sent Events at six connections per domain. That limit put upload progress indicators, live notifications, and activity feeds effectively out of reach. Over HTTP/2, the number of concurrent streams is negotiated with the server and defaults to 100. If you gave up on testing a streaming workflow, it is worth another look.

This change currently only applies to Chromium-based browsers. Firefox and WebKit continue to use HTTP/1.1 in this release. 

This is a big improvement to Cypress, but also a big change. If you encounter any issues, you can opt out of the new behavior by passing forceHttp1: true to assist in debugging. Open an issue in the cypress repository if you see a bug that’s only present in the new default behavior.

Faster visibility checks

Cypress 16 uses a more modern element visibility algorithm as its default. It calls the browser's built-in visibility check first, then confirms coverage with an adaptive point-sampling approach, which avoids most of that layout thrashing. This shipped as an experiment and now everyone gets it without opting in.

Every time Cypress asserts that an element is visible, it runs a visibility algorithm. The legacy version walked up the ancestor tree reading CSS properties at each level, which repeatedly triggered layout recalculation in the browser. On a large, deeply nested component tree, with a suite full of visibility assertions, that cost adds up quietly across every run. The modern algorithm is meant to speed up those checks.

If you need time to migrate, you can restore the old behavior by setting visibilityStrategy: 'legacy' in your global, suite, or test Cypress configuration.

Typing at full speed

In Cypress 16 the default keystroke delay for cy.type() is 0ms, so forms fill as fast as your application can accept the input. Previously cy.type() set the delay to 10ms to simulate human-delayed typing, but for a suite with hundreds of test with forms, this increased run duration doesn't add much value. This default change will improve overall test durations. Applications that genuinely depend on inter-keystroke timing, such as inputs with aggressive debouncing or components that re-render on every keystroke, can set the delay back to its previous value globally or per command.

Long runs that don’t crash the renderer process

In a long run, the browser accumulates memory across tests. As pressure builds, the browser slows down, and in bad cases it crashes before the suite finishes. Since Cypress 12.4.0 the recommendation has been to set the experimentalMemoryManagement flag, which clears memory between tests in Chromium browsers. We have recommended it for years and run it on our own suites, but as an experimental flag most teams never found it.

In Cypress 16 it is stable, renamed to manageBrowserMemory, and on by default.

With manageBrowserMemory enabled, Cypress monitors renderer memory during a run in Chromium-based browsers and triggers garbage collection before the browser runs out, which is the difference between a long spec finishing and a "We detected that the Chrome Renderer process just crashed" failure.

It is not a universal speed-up: clearing memory takes time, and on a light suite that was never near a memory ceiling, that work is overhead rather than savings. The configuration option stays a single option available to turn off. Long specs and memory-limited CI containers benefit the most, and neither needs configuration anymore.

Fewer flaky tests, fewer reruns

Not all wasted time looks like a slow test. A test that retries on every run costs you on every run, and a test that fails on flake costs a developer the investigation plus a full re-trigger of CI.

Cookie and storage reads were a common source of that flake. Reading a cookie set asynchronously, after a redirect or a server round trip, could miss it by a few milliseconds and fail. In Cypress 16, cy.getCookie(), cy.getCookies(), cy.getAllCookies(), cy.getAllLocalStorage(), and cy.getAllSessionStorage() are query commands. Assertions chained to them re-read the underlying state and retry until they pass:

cy.getCookie('session_id').should('exist')

That assertion now waits for the cookie the same way cy.get() waits for an element. One less race, and one less reason for a green suite to go red for no good reason.

A faster foundation

Some of the gain in this release is not a feature at all. Cypress 16 moves onto a newer runtime and browser foundation: Node.js 24 under the hood, Chromium 146, and current major versions of the component testing toolchain, including Vite 8, and Angular 21 and 22. Each of those brings its own performance work that your tests inherit for free.

We also removed some unnecessary code. experimentalSourceRewriting, an experiment that could make a single cy.visit() five to ten times slower for the teams who enabled it, is gone without losing the improvements it was meant to enable. So are several long-deprecated APIs.

Secrets stay out of the browser

Cypress.env() has been removed. It hydrated every configured environment variable into the browser, which meant a single secret in your environment was readable by application code, third-party scripts, and any cross-origin context your test entered.

Cypress 16 splits that into two APIs with different guarantees:

  • cy.env() reads sensitive values asynchronously and keeps them in the Node process. Only the keys you request cross into the test.
  • Cypress.expose() publishes non-sensitive values, such as feature flags or a public API base URL, for synchronous access.

The env key is no longer accepted in per-test or per-suite configuration overrides, and the allowCypressEnv option that gated the deprecation has been removed.

Related to the same goal, cypress info no longer prints HTTP_PROXY, HTTPS_PROXY, NO_PROXY, or any CYPRESS_* environment variable. The command was a common thing to paste into a public bug report, and it now reports only file paths, version, and system information.

Other breaking changes

Cypress 16 also includes some other breaking changes, each of which has a direct replacement in Cypress today.

  • The Electron browser is deprecated. Electron still works in Cypress 16, see our blog post on the Electron browser deprecation for full detail on how to migrate off.
  • cy.exec() and execTimeout are removed. Use cy.task() instead.
  • cy.end() is removed. You can delete the .end() calls.
  • viewportWidth, viewportHeight, and blockHosts can no longer be set with Cypress.config() during test execution. Set them in the test configuration of a describe, context, or it block instead.
  • CoffeeScript support is removed.
  • Angular 18, 19, and 20 are no longer supported for Component Testing
  • Vite 5, 6, and 7 are no longer supported for Component Testing
  • Next.js 14 is no longer supported for Component Testing

How to upgrade to Cypress 16

The Cypress 16 migration guide covers every change with concrete steps, and the changelog has the full list.

Additionally, the Cypress 16 migration guide includes a ready-made prompt to paste into any AI coding assistant such as Claude Code, Cursor, Copilot and it'll work through the upgrade with you: checking your Node version and framework requirements, updating the dependency, and finding the code that needs to change. Review what it does and commit.

Not on the latest Cypress 15 yet?

Not ready to upgrade? There's still a faster win available to you: catch up on 15 first. We've been shipping performance fixes throughout 15, as fast as we could get them out, in minor releases you get for free.

Upgrading across several majors used to mean reading a lot of guides. It doesn't anymore. Every major version from 10 forward has a ready-made prompt in the migration guide. Open the section for the version you're moving to, copy the prompt into whatever AI coding assistant you use, and it'll work through the upgrade with you: checking your Node version and framework requirements, updating the dependency, and finding the code that needs to change. Review what it does, one major at a time, and keep going until you land on the latest.

Here's some of what you're missing if you've been sitting on an older version:

Features

  • Let your AI agent run, watch, and fix Cypress tests locally. (15.21.0). cypress tap is an extension of the Cypress CLI that lets you or an agent interact with an open-mode Cypress session and read its context from the terminal. It’s included for free in the Cypress app. With it, an agent can run a spec and poll its status, then read the failing test's Command Log and error as the Cypress app shows them. It can also inspect the DOM of the application under test at the moment a command is run.
  • Turn natural language steps into self-healing Cypress commands (15.13.0). cy.prompt allows you to use natural language to construct tests and let them self-heal selectors as they run.
  • Record, assert, and verify with AI doing the heavy lifting (15.11.0). Cypress Studio lets you generate and extend end-to-end tests by recording real interactions in your app. Studio AI adds a layer on top: as you record, it watches what changes in the UI and recommends assertions automatically. You review them, keep what fits, and save.
  • Hide HTTP requests in the Command Log (15.4.0). When you don’t need to inspect network calls, you can hide them directly from the Command Log.
  • HTTP QUERY method (15.20.0). cy.request() and cy.intercept() now accept the HTTP QUERY method. Previously it was rejected as an invalid method in the browser.
  • Escape key support in cy.press(). Simulate pressing the Escape key to drive dismiss and cancel behavior in the browser.
  • --pass-with-no-tests command line flag (15.11.0). Exit runs with a success code when no spec files are found. Useful in CI pipelines where specs may be conditionally generated or filtered out at runtime.
  • Detect runs failing due to Cloud API errors during recording (15.5.0). With --posix-exit-codes, Cypress can return additional exit conditions when Cypress fails due to network errors when connections to Cypress Cloud are required.
  • experimentalRunAllSpecs for component tests (15.9.0). The experimentalRunAllSpecs option can now be used for component testing as well as e2e testing.
  • Bun package manager support (15.17.0). The cypress npm package can now be installed and invoked with Bun.
  • Video recording in Firefox (15.17.0). Video recording now correctly produces a video in Firefox.

Performance

  • Fixed crashes from noisy applications (15.19.0, 15.18.0). A ResizeObserver loop firing every animation frame, or the same uncaught exception thrown over and over, could exhaust memory. Repeated identical exceptions now collapse into one command log entry.
  • Renderer crashes on text-heavy pages resolved (15.20.0). Visibility checks serialized an element's entire text subtree once per overflow-hidden ancestor, which could exhaust renderer memory and take the browser down with it.
  • A memory leak on every browser message removed (15.19.0). Each message Cypress sent to a Chromium browser leaked a little memory until the spec ended. Long, command-heavy or network-heavy specs could crash from it alone.
  • Open mode memory cleared (15.14.1, 15.14.0). Each spec rerun held onto the previous Mocha runner and everything it referenced, and command log data stuck around after tests aged out of numTestsKeptInMemory.
  • A sluggish Command Log made speedy (15.17.0, 15.14.2, 15.7.1). Hovering over a long test triggered a style recalculation across every command in the list, scrolling registered duplicate listeners, and highlighting elements in a snapshot did more style work than it needed to.
  • Wasted CI cycles removed (15.20.0, 15.13.1, 15.5.0). Memory sampling spawned helper subprocesses on constrained containers, cypress run made git calls it only needed for the GUI, and spec transitions did more storage cleanup than necessary.

A ton of bugfixes. We closed out over 200 issues in the lifetime of Cypress 15. You can see all the bugfixes in the Cypress app changelog.

Common questions about Cypress 16

Is Cypress 16 a breaking release? Yes. It removes several long-deprecated APIs, changes some defaults, and raises minimum versions for Node.js, Vite, and Angular. Every change has migration steps in the Cypress 16 migration guide.

Where can I see the full list of changes in Cypress 16? The full list of changes for Cypress 16 is in the Cypress App changelog.

Is there a migration guide for Cypress 16? There’s a migration guide for Cypress 16, including a ready-made prompt that walks your AI coding assistant through the migration.

Does Cypress support HTTP/2? Yes, as of Cypress 16. HTTP/2 is enabled by default in Chromium-based browsers, including Chrome, Chromium, and Edge. Electron, Firefox, and WebKit continue to use HTTP/1.1.

Does Cypress support HTTP/3? Yes, as of Cypress 16. In Chrome, Chromium, and Edge, your application connects to your server directly and negotiates whatever protocol the server supports, including HTTP/3, exactly as it does in production. If your server serves HTTP/3 to real users, that is the protocol under test. No configuration is needed. Electron, Firefox, and WebKit continue to use HTTP/1.1.

Do I need to change my tests to use HTTP/2? No. Requests use HTTP/2 automatically when the server supports it.

Does HTTP/2 change how cy.intercept() works? The API is unchanged. You match, stub, modify, and wait on requests exactly as before, and most suites need no edits. Under the hood, Chrome, Chromium, and Edge now intercept traffic on the native browser network, so a few transport-level details report differently: req.httpVersion is no longer reported, compression headers such as content-encoding no longer appear on responses (the body is already decoded), revalidated responses report 200 instead of 304, and res.statusMessage is an empty string over HTTP/2 and HTTP/3, which carry no reason phrase. Assertions on response bodies, status codes, and your application's behavior work as they did before, and Firefox, WebKit, and Electron are unaffected. Every difference is documented with a before-and-after example in the native network interception guide.

Will Cypress 16 make my tests faster? It depends on where your time is going. The gains are largest for suites with request-heavy pages, lots of typing, many visibility assertions, or long runs that hit memory pressure. If your suite is slow because of long waits, repeated UI logins, or real network calls, those causes are unchanged, and the test performance guide is the place to start.

Why is Cypress.env() removed? This change is driven by the way Cypress.env() hydrates all Cypress environment values into the browser context, including values a test may never read. This makes it easy to unintentionally expose more data than intended. Read our blog post on migrating to cy.env() and Cypress.expose()

Why is cy.exec removed? cy.task can do everything that cy.exec does and cy.task runs in Node without depending on the operating system, shell, or terminal of the machine running Cypress. This simplifies support of running code in Node and removes the bugs in cy.exec that existed due to variability in the environment.

Is the Electron browser removed in Cypress 16? No. Electron is deprecated and warns when selected, but it still runs tests. Removal will come in a later major version that we have not scheduled. Read our blog post on the Electron browser being deprecated in Cypress for full details on why and how to migrate.

Why can’t I set viewportWidth and viewportHeight with Cypress.config? Setting the viewportWidth and viewportHeight via Cypress config resulted in unexpected behavior: it actually changed the viewport size of the next test rather than the current one. The viewport change was also not reflected in Test Replay if you were recording. Use cy.viewport() or set viewportHeight and viewportWidth in the test configuration of a describe, context, or it block instead.

Why can’t I set blockHosts with Cypress.config? Setting the blockHosts via the Cypress.config resulted in unexpected behavior - it actually affected the next test rather than the current one. Set blockHosts in the test configuration of a describe, context, or it block instead.

How do I get my tests to type like a real user again in Cypress 16? In Cypress 16, the default delay for typing was set to 0ms. You can set it back to 10ms or any other number you want via the keystrokeDelay option of the Cypress.Keyboard.defaults API or by passing a delay argument to cy.type().

Cypress.Keyboard.defaults({
  keystrokeDelay: 10,
})
cy.get('input').type('slow typing', { delay: 10 })

What replaces the experimentalMemoryManagement config? In Cypress 16, the experimentalMemoryManagement configuration was replaced by a manageBrowserMemory configuration option which defaults to true. Cypress samples the browser's memory usage on an interval while your tests run, and forces a garbage collection before the next test only when a sample has crossed a memory threshold.

This flag is not a blanket speed improvement. Clearing memory between tests takes time, paid on every test transition. If your suite is not under meaningful memory pressure, you can opt out to potentially save the overhead. You should measure the performance impact before and after disabling the flag.

Why is my test failing in Cypress 16 with "element is not visible"? In Cypress 16, the default element visibility strategy was changed to be more accurate, more reliable, and more performant. That change can affect existing visibility assertions. When you encounter a test that fails under the modern algorithm, prefer updating the assertion to verify the same user-visible behavior in an algorithm-agnostic way. You can set visibilityStrategy: ‘legacy’ to opt into the old visibility strategy while you migrate your tests. See our visibility strategy documentation document for full details and tips for updating. 

Why does Cypress.Commands.overwrite('getCookie', ...) fail in Cypress 16? Because cy.getCookie(), cy.getCookies(), cy.getAllCookies(), cy.getAllLocalStorage(), and cy.getAllSessionStorage() are now query commands, overwriting them requires Cypress.Commands.overwriteQuery() instead of Cypress.Commands.overwrite().

Did the timeout for cookie commands change? Cookie and storage reads now use defaultCommandTimeout like other queries, instead of responseTimeout. Cookie action commands such as cy.setCookie() still use responseTimeout. If a read needs more time, pass a timeout option to the command or the chained assertion.

Which framework versions does component testing support in Cypress 16? Component testing supports Angular 21 and 22 (including zoneless applications), Vite 8, and Next.js 15 and 16. Angular 18 through 20, Vite 5 through 7, and Next.js 14 are no longer supported.