ScreenshotNeo

BlogHow-to

How to Take Cypress Screenshots of a Website Behind Login

Restore and verify an authenticated Cypress session, capture the right page area, and troubleshoot login and screenshot issues in local runs and CI.

By the ScreenshotNeo team4 October 20269 min read

To take a Cypress screenshot of a page behind login, establish the user’s authenticated state, visit the protected route, verify that the expected page loaded, and then call cy.screenshot(). For repeatable tests, put login setup in cy.session() so Cypress can restore cookies and browser storage. A screenshot command captures the current page; it does not log in for you.

1. Choose how the test logs in

Use an API login when your application provides a suitable test endpoint and you want to avoid replaying the login form in every test. Use the UI login when the login journey itself is what you need to exercise. Cypress documents both patterns, and describes HTTP login as the faster option where it applies. Cypress API testing guide

Approach Use it when Trade-off
API login Your app exposes a supported test or authentication endpoint. Fast setup, but does not test the login form’s user-facing behavior.
UI login The login interaction is part of the behavior under test, or no suitable API exists. Exercises the browser flow, but usually requires more steps and can be more sensitive to UI changes.

The examples below assume Cypress is installed and configured for your application, a test account exists, and the app has an API login endpoint at /api/login plus a current-user endpoint at /api/me. Replace these paths and assertions to match your app’s authentication design. Store credentials in Cypress environment configuration or CI secrets; do not put real credentials in source control.

2. Reuse and validate an authenticated session

Define a login helper that creates or restores a session, then validate it by requesting an endpoint that only an authenticated user can access. The session ID should identify the account or role without containing its password or token: Cypress can display session IDs in the reporter.

const loginAs = (username) => {
  cy.session(username, () => {
    cy.request('POST', '/api/login', {
      username,
      password: Cypress.env('TEST_PASSWORD'),
    })
  }, {
    validate() {
      cy.request('/api/me').its('status').should('eq', 200)
    },
  })
}

describe('authenticated page screenshots', () => {
  it('captures the account page', () => {
    loginAs(Cypress.env('TEST_USERNAME'))

    // cy.session restores authentication state; visit the page to capture.
    cy.visit('/account')
    cy.contains('Account').should('be.visible')

    cy.screenshot('account-page', { capture: 'viewport' })
  })
})

This example uses cy.request() to establish cookie-based authentication. Cypress uses the browser cookie jar for requests, so cookies set by the login response are available to the app. If your app uses a different authentication mechanism, adapt setup and validation accordingly. See the official cy.session() API reference and API testing guide.

What session caching does and does not do

  • cy.session() saves and restores cookies, localStorage, and sessionStorage.
  • It does not choose the page to capture. Visit the protected route after the session has been set up or restored.
  • Cypress clears cookies and web storage before running the session setup callback. With test isolation enabled, the page is also cleared.
  • A validate callback checks that restored authentication still works. If validation fails on restore, Cypress reruns setup; if it fails right after setup, the test fails.
  • cacheAcrossSpecs: true can reuse a session across specs in one Cypress run on the same machine. A new run starts with an empty cache, and parallel CI machines establish their own sessions.

3. Verify the page, then choose the capture area

After cy.visit(), assert something that appears only on the authenticated page. This catches expired sessions, redirects to login, and unexpected page states before they become misleading screenshots. Wait for the content you intend to show; Cypress’s screenshot command is asynchronous, and a changing page can shift during capture.

cy.visit('/account')
cy.location('pathname').should('eq', '/account')
cy.get('[data-testid="account-heading"]').should('be.visible')
cy.screenshot('account-page', { capture: 'viewport' })

Use a stable selector from your application in place of [data-testid="account-heading"]. Avoid relying only on a URL check if the app can render an error or loading state at that URL.

Capture option Example What it captures
Viewport cy.screenshot('account', { capture: 'viewport' }) The currently visible browser viewport.
Full page cy.screenshot('account-full', { capture: 'fullPage' }) The application page from top to bottom.
Runner cy.screenshot('account-runner', { capture: 'runner' }) The Cypress runner context, useful for debugging and includes runner UI.
Element cy.get('[data-testid="account-card"]').screenshot('account-card') The selected element.

For full-page captures, make sure the page has finished rendering and any required content has loaded. For element captures, ensure the selector matches the intended element and that it is visible. Cypress disables timers and animations by default during capture to reduce movement. The command reference documents capture modes, options, and element screenshots: cy.screenshot().

4. Capture through the login form when needed

If the user-facing login flow is part of the test, put that interaction in the session setup callback. The following is a template: replace the selectors and completion assertion with selectors that match your application.

const loginThroughUI = (username) => {
  cy.session(username, () => {
    cy.visit('/login')
    cy.get('[name="username"]').type(username)
    cy.get('[name="password"]').type(Cypress.env('TEST_PASSWORD'), {
      log: false,
    })
    cy.get('button[type="submit"]').click()
    cy.get('[data-testid="account-heading"]').should('be.visible')
  }, {
    validate() {
      cy.request('/api/me').its('status').should('eq', 200)
    },
  })
}

describe('account screenshot', () => {
  it('captures the authenticated account page', () => {
    loginThroughUI(Cypress.env('TEST_USERNAME'))
    cy.visit('/account')
    cy.get('[data-testid="account-heading"]').should('be.visible')
    cy.screenshot('account-page', { capture: 'fullPage' })
  })
})

Whether this exact interaction is appropriate depends on the app. Some login flows include redirects, multi-factor steps, or identity-provider pages; encode the supported test flow for your environment and validate the resulting application session before capture.

5. Configure screenshot files and protect private data

Screenshots default to Cypress’s cypress/screenshots folder. Named screenshots are saved under the adjusted spec path. Duplicate names are incremented unless you set overwrite: true. Cypress clears the screenshot folder before cypress run by default; set trashAssetsBeforeRuns: false in configuration only when preserving that folder across runs is intentional. See the screenshots and videos guide and configuration reference.

Authenticated pages can contain personal details, account identifiers, or secrets. Cypress’s blackout option can hide selected elements for supported application captures. Runner captures do not honor blackout selectors, and Cypress uses runner mode for automatic failure screenshots. Review images before sharing or uploading them, and avoid including sensitive data in test fixtures where possible.

cy.screenshot('account-redacted', {
  capture: 'viewport',
  blackout: ['[data-testid="email"]', '[data-testid="account-number"]'],
})

Do not assume blacking out a selector covers every capture mode or every sensitive value. Check the command options and Cypress.Screenshot API for the behavior relevant to your capture.

6. Run locally and in CI

You can take manual screenshots in both cypress open and cypress run. Cypress automatically captures screenshots for test failures during cypress run, including CI runs; it does not automatically capture failure screenshots in cypress open. Set screenshotOnRunFailure: false if you need to disable automatic failure captures. In CI, keep the screenshots directory as a build artifact using your CI provider’s artifact mechanism, or view CI screenshots through Cypress Cloud.

// cypress.config.js (CommonJS)
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  screenshotOnRunFailure: true,
  trashAssetsBeforeRuns: true,
  // screenshotsFolder: 'cypress/screenshots', // Cypress default
})

Keep session expectations realistic in CI: session caching is local to a run and machine, so each parallel machine needs to establish its own authenticated state. Ensure the test account and authentication service are available in the CI environment, and provide secrets through the CI secret store.

7. Troubleshooting

Symptom Likely cause Fix
Screenshot shows the login page The session was not established, expired, or the app redirected after navigation. Validate authentication in cy.session(), visit the target route after session restoration, and assert an authenticated-only element before capture.
cy.session() setup fails The login request or UI flow does not match the app, or test credentials are unavailable. Check the endpoint, request payload, response, selectors, and CI secrets. Make the setup callback reflect the app’s actual login mechanism.
Session restores but the page is unauthenticated The saved state is stale or the application requires state not covered by the current setup. Use a meaningful validate callback and update setup to establish the app’s required cookies or browser storage.
Page is blank or partially rendered The screenshot ran before the target content appeared or finished loading. Wait for a stable, visible page-specific selector before calling cy.screenshot().
Screenshot has duplicate suffixes or an unexpected path Names are duplicated, or Cypress nests named captures by spec path. Use unique names; enable overwrite: true only when replacing the previous file is intended. Check the configured screenshots folder and spec-relative path.
Previous screenshots disappear in CI Cypress clears the screenshots folder before a run by default. Upload the directory as a CI artifact; set trashAssetsBeforeRuns: false only if retaining it between runs is required.
Failure image includes Cypress controls or private page content Automatic failure screenshots use runner capture, where blackout selectors do not apply. Review failure artifacts before sharing, minimize sensitive test data, and consult the screenshot API behavior for supported capture modes.
Tests repeat login on every parallel worker Session caches do not cross machines or separate Cypress runs. Expect each worker to create its own session, or arrange test data and authentication so each machine can log in independently.

8. Performance, reliability, and cost

Session reuse avoids replaying the full authentication flow whenever a valid session can be restored. API setup is often the quickest option when supported, while UI setup covers the login journey. Neither removes the need to validate state: a stale or misconfigured session can waste test time and produce a screenshot of the wrong page. Cypress notes that screenshot capture takes around 100 ms in its command reference, and the page can change during that interval, so wait for the intended content immediately before capture.

For reliable output, use a dedicated test account, deterministic page data, explicit visible-content assertions, and unique screenshot names. In CI, plan for each parallel machine to establish its own session and retain screenshots through CI artifacts if you need them after a run. Screenshot execution costs depend on your own CI and Cypress setup; the cited Cypress documentation does not specify a universal per-capture price.

Or skip the browser setup

If you need a screenshot of a publicly reachable page and do not need Cypress to exercise your app’s login journey, ScreenshotNeo is a website screenshot API and MCP server. One request returns an image or PDF. It does not replace an authenticated Cypress test for a private page that requires your app’s login state.

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}`)
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`)
await Bun.write('shot.webp', res)

See the ScreenshotNeo API docs for request options and response details. Before capture, it accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.

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

FAQ

Does taking a Cypress screenshot log the user in?

No. Establish authentication first, then visit and verify the page before capturing it.

Can a Cypress session be shared across CI machines?

No. Cross-spec reuse applies within one run on one machine; parallel machines need to establish their own sessions.

Can I capture just one component?

Yes. Select the element with cy.get() and call .screenshot() on it.

Does ScreenshotNeo capture a page behind my app’s login?

This article’s ScreenshotNeo example is for a publicly reachable URL. For an app that requires a private authenticated browser session, use the Cypress workflow above.