ScreenshotNeo

BlogHow-to

How to Use cy.session() to Speed Up Cypress Authentication

Use cy.session() to cache Cypress authentication safely, validate restored sessions, and avoid repeating login flows across tests and specs.

By the ScreenshotNeo team4 October 20269 min read

cy.session() speeds up Cypress authentication by caching cookies, localStorage, and sessionStorage after a successful login. On later calls with the same session ID, Cypress restores that browser state instead of repeating the login flow. Put the login and a success assertion in setup, validate the restored session, and call cy.visit() after cy.session() to open the page under test when test isolation is enabled.

1. How cy.session() works

The command has this form:

cy.session(id, setup, options)

id identifies the authentication state. setup creates it, and optional validate checks that it still works. Cypress saves browser cookies and storage after setup and validation succeed. When the same ID is used again while its cached state is valid, Cypress restores it and skips setup.

With test isolation enabled, Cypress clears the page and browser context when caching or restoring a session. A restored login does not mean the test page is already open: visit the route your test needs after the session command.

2. Reusable UI login session

This example puts the full UI login flow and its success assertion inside setup. It reads credentials from Cypress environment configuration, suppresses password logging, and checks authentication through a protected API endpoint.

// cypress/support/commands.js
Cypress.Commands.add('login', (username, password) => {
  cy.session(
    ['login', username],
    () => {
      cy.visit('/login')
      cy.get('[data-test=name]').type(username)
      cy.get('[data-test=password]').type(password, { log: false })
      cy.get('form').contains('Log In').click()
      cy.url().should('contain', '/login-successful')
    },
    {
      validate() {
        cy.request('/api/user').its('status').should('eq', 200)
      },
    }
  )
})

// cypress/e2e/account.cy.js
describe('account', () => {
  it('shows the account page', () => {
    cy.login(Cypress.env('username'), Cypress.env('password'))
    cy.visit('/account')
    cy.contains('Account').should('be.visible')
  })
})

Configure the credentials outside source control, for example through your CI secret store or Cypress environment configuration. Cypress documents accessing environment values during session setup and logging controls in its environment API reference. Do not put a password or token in the session ID: IDs can appear in reporting and debugging tools.

Why the success assertion belongs in setup

Only cache a state after login actually finishes. A URL assertion, authenticated page assertion, or other reliable success signal inside setup prevents a redirect or failed form submission from being mistaken for a valid session. Validation serves a different purpose: it checks that a saved session remains authenticated when Cypress reuses it.

3. Use an API login instead of a UI flow

If the application exposes a supported login endpoint, a test can create the session through cy.request(). This avoids spending browser time on a form for every fresh session. For cookie authentication, the browser’s cookie jar receives server-set cookies. For bearer-token authentication, save the returned token in browser storage so that the app can use it, then validate against an authenticated endpoint.

// Cookie-based API login
Cypress.Commands.add('loginByApi', (username, password) => {
  cy.session(
    ['api-login', username],
    () => {
      cy.request('POST', '/api/login', { username, password })
        .its('status')
        .should('eq', 200)
    },
    {
      validate() {
        cy.request('/api/user').its('status').should('eq', 200)
      },
    }
  )
})

// Use it in a test
it('opens the account page', () => {
  cy.loginByApi(Cypress.env('username'), Cypress.env('password'))
  cy.visit('/account')
  cy.contains('Account').should('be.visible')
})

For a bearer token, adapt the setup to your app’s response shape and storage convention:

cy.request('POST', '/api/login', { username, password }).then(({ body }) => {
  window.localStorage.setItem('authToken', body.token)
})

That storage snippet requires a browser window. If the API login does not navigate, visit a same-origin page first, then set the token with cy.window():

cy.visit('/')
cy.request('POST', '/api/login', { username, password }).then(({ body }) => {
  cy.window().then((win) => {
    win.localStorage.setItem('authToken', body.token)
  })
})

Place the request and storage work inside the session’s setup callback, and keep the success assertion and validation appropriate to the app. The token itself must not appear in the session ID.

4. Choose a safe session ID and validation

Session IDs

The ID must distinguish every setup input that can produce a different authenticated state. Cypress accepts strings, arrays, and objects, and deterministically serializes array and object IDs. Include non-secret identity dimensions such as:

  • Username or account identifier when tests log in as different users.
  • Role when role selection changes permissions or resulting session state.
  • Tenant or organization when users can belong to multiple tenants.
  • Authentication method or environment when the setup differs.

For example, ['login', username, role, tenant] separates states without exposing a password. Do not include passwords, access tokens, refresh tokens, or other secrets.

Validation

Use a check that fails when the user is no longer authenticated, such as an authenticated-user API request or a visit to a protected page followed by an authentication assertion. If validation fails while restoring a cached session, Cypress reruns setup. If validation fails immediately after setup, the test fails because Cypress cannot establish a valid session.

Choose a validation endpoint that has a clear authenticated response. A public endpoint that returns success for anonymous users cannot detect an expired session. Avoid checks that pass before the application has finished establishing authentication.

5. Share sessions across spec files

Set cacheAcrossSpecs: true when specs in the same Cypress run on the same machine can reuse an identical session:

cy.session(
  ['login', username],
  setupLogin,
  {
    validate: validateLogin,
    cacheAcrossSpecs: true,
  }
)

This global cache lasts for one cypress run on one machine. It does not carry over to a later run or to another parallel CI machine. Each participating spec must call the session with consistent ID, setup, validation, and option values. Put the session definition in a shared custom command or helper so those details do not drift between specs.

6. Configuration and isolation choices

Choice Effect When to use it
Default session caching Reuse matching session state within the applicable spec context. Most tests that repeat authentication within a spec.
cacheAcrossSpecs: true Make the session available across specs in one run on one machine. Specs share the same test account state and run in the same Cypress process environment.
validate() Check whether restored state is still authenticated; failed restore validation triggers setup again. Sessions can expire or be invalidated during a run.
testIsolation Controls whether Cypress clears the page and browser context when caching and restoring. Keep isolation enabled for independent tests; change only when the suite is designed around the resulting shared state.

Disabling test isolation is not a universal speed setting. It can let browser state or page state leak between tests, create order dependencies, and behave differently when running an individual test with .only(). Keep each test responsible for visiting its own route and establishing the required state.

7. Performance, reliability, and cost

The speed benefit comes from avoiding repeated login work, especially browser-driven form entry and redirects. Cypress’s performance guide gives an illustrative estimate of 2–5 seconds for a full login flow per test and 3–8 minutes of authentication overhead across 100 tests. Those are Cypress’s published typical estimates, not a guarantee for a particular application or suite. Actual time depends on login latency, redirects, validation, and how many distinct session IDs the suite needs.

  • Reduce unnecessary UI work: use an API login when the app provides a supported, representative authentication endpoint; retain UI login tests where the login interface itself is under test.
  • Keep validation cheap and meaningful: a small authenticated-user request is often less work than replaying a full page journey, while still detecting expiration.
  • Use stable test accounts: account lockouts, changing tenant membership, MFA, or server-side revocation can invalidate cached state and cause setup to run again.
  • Keep tests independent: reuse authentication state, but let each test visit its own page and assert its own behavior.
  • Account for CI workers: parallel machines have separate caches and must establish their own sessions.

cy.session() adds no Cypress-specific usage charge. The practical cost is test runtime and CI compute: successful reuse reduces repeated login work; failed validation or distinct identities cause setup to run again. Cypress’s performance guide discusses session reuse in optimizing test performance.

8. Troubleshooting common failures

Symptom Likely cause Fix
Blank page or commands run against no app Test isolation cleared the page as part of caching or restoring the session. Call cy.visit('/your-route') after cy.session() returns.
401 after a session restore The cached session expired, login was not complete before it was cached, or the app depends on state not restored by the session. Assert successful login in setup; validate with a protected endpoint and make sure the authentication state is in cookies or browser storage.
Wrong user or tenant appears The ID omits a non-secret input that changes the session, so a different setup reuses the same cache key. Add username, role, or tenant to the ID. Keep secrets out of it.
Login happens again in another spec Cross-spec caching is not enabled, the calls differ, or the spec is running on another machine. Use cacheAcrossSpecs: true consistently in the same run and share one session helper. Expect separate setup on parallel machines.
Session is cached before redirect completes Setup has no assertion proving that login succeeded. Wait on a deterministic URL, authenticated element, or API response inside setup.
Validation always passes for logged-out users The validation URL is public or does not check identity. Use an endpoint or protected page whose success depends on being authenticated.
Tests pass only in suite order Disabled isolation or shared mutable application state creates coupling. Restore isolation where practical; visit the required page and establish any non-session state in each test.

9. Or skip the browser setup

For screenshots of authenticated or public pages, ScreenshotNeo is a website screenshot API and MCP server: one GET request returns a PNG, JPEG, WebP, or PDF. It is separate from Cypress session caching. Its API documentation covers the request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo accepts cookie and consent banners as a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the page verdict and billing status in headers. An MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. All features are available on every plan.

cURL, Python, and Node.js

The same capture can be requested from common scripting environments. See the ScreenshotNeo docs for options such as full-page capture, element selection, viewport and device settings, wait conditions, headers, cookies, custom CSS or JavaScript, caching, signed links, asynchronous jobs, bulk capture, and PDF settings.

# 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 (ES modules or a runtime with global fetch)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

Keep API keys out of source control. For authenticated-page screenshots, configure the supported authentication inputs for the capture request; a Cypress browser session is not automatically shared with the ScreenshotNeo API.

Sign up free for 1,000 screenshots a month with no card.

10. FAQ

Does cy.session() replace logging in for every test?

It skips setup when Cypress can restore a valid cached session with the same ID. A new ID or failed restore validation requires setup again.

Can I share a session between separate Cypress runs?

No. cacheAcrossSpecs supports specs in one run on one machine, not later runs or other parallel machines.

Which Cypress versions support cy.session()?

Cypress documents that the command became available by default in version 12.0.0 after the experimental session and origin feature was removed. Check the API reference and your installed version for current version-specific details.

Should I use UI login or API login?

Use UI login when the login interface is part of what the test must cover. Use an API login when it is supported by the app and your goal is to establish authentication efficiently for unrelated page tests.

Official Cypress references