ScreenshotNeo

BlogHow-to

Why Does Chrome Headless Screenshot Capture a Login Page Instead?

Headless Chrome captures the session its browser context reaches. Check profile isolation, stored authentication, redirects, and capture timing to find why a login page appears.

By the ScreenshotNeo team4 October 20267 min read

Chrome Headless captures the page state reached by the browser profile and context that performed the navigation. It does not automatically borrow cookies, local storage, or a signed-in session from a separate visible Chrome profile. A login page usually means the automation is unauthenticated, its session was cleared or rejected, or the screenshot was taken before login and redirects finished.

The exact cause depends on your site, browser setup, and capture code. Start by checking which profile and browser context the run uses, then verify the session state and the page reached at capture time.

1. Confirm the browser profile and context

Cookies and local storage belong to a browser profile and origin. Browser contexts isolate storage from one another, so signing in through one context does not sign in another. A new automated browser or incognito context should be treated as a separate session unless you explicitly provide authentication state.

In Puppeteer, userDataDir selects a profile directory for a launched browser. Use the same intended profile for the authenticated flow and screenshot navigation. A persistent profile can retain state between runs, but the session still needs to be valid and compatible with the site.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  headless: true,
  userDataDir: './chrome-profile',
});

const page = await browser.newPage();
await page.goto('https://example.com/account', {
  waitUntil: 'domcontentloaded',
});

console.log('Final URL:', page.url());
await page.screenshot({ path: 'account.png', fullPage: true });
await browser.close();

Keep profile directories isolated between unrelated users or jobs. Do not run concurrent browser processes against a profile directory unless your browser setup explicitly supports that usage.

2. Check whether session storage is being cleared

Some tools intentionally start with a clean browser state. Lighthouse documents that its default runs load a page as a new user, without previous session or storage data. This is a Lighthouse-specific default, not a general rule that Chrome Headless always clears authentication.

Determine how your application authenticates. It might rely on cookies, local storage, IndexedDB, a server-side session, or a combination. Restoring one cookie may not be enough if the application also requires another storage value or a completed redirect flow. For Lighthouse, follow its authenticated-page workflow and check whether the run is resetting storage.

3. Verify the session at the target origin

Before capturing, inspect whether the browser context has the expected site state and whether the site still accepts it. Cookie APIs can help inspect or set cookies, but the required names, attributes, and validity rules are specific to the target application.

// Puppeteer: inspect cookies available to this page/context
const cookies = await page.cookies('https://example.com');
console.log(cookies.map(({ name, domain, expires }) => ({ name, domain, expires }))); 

Check that the cookie belongs to the correct domain and path, has not expired, and is available in the same context that opens the protected page. Avoid printing cookie values into logs: session cookies are credentials.

4. Distinguish HTTP authentication from an application login

Puppeteer’s page.authenticate() supplies credentials for HTTP authentication challenges. It is not a general login mechanism for a website form, OAuth flow, multi-factor prompt, or JavaScript-driven session.

// For HTTP Basic/Digest authentication only
await page.authenticate({
  username: process.env.HTTP_USER,
  password: process.env.HTTP_PASSWORD,
});
await page.goto('https://protected.example.com');

For a normal application login, use the application’s supported login flow or restore the necessary state in a controlled test environment. Account for any MFA, consent, or identity-provider redirects the site requires.

5. Wait for login and navigation to finish

A screenshot taken during a redirect or before a single-page application finishes rendering can show a login page or an intermediate state. Wait for the navigation caused by the login action and then check an application-specific signal, such as a user menu or account heading. A fixed sleep alone does not prove authentication succeeded.

// Example for a site whose login form navigates after submission.
await page.goto('https://example.com/login');
await page.locator('input[name="email"]').fill(process.env.APP_USER);
await page.locator('input[name="password"]').fill(process.env.APP_PASSWORD);

await Promise.all([
  page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  page.locator('button[type="submit"]').click(),
]);

// Replace this selector with a reliable, site-specific authenticated signal.
await page.waitForSelector('[data-testid="account-menu"]', { timeout: 15000 });
console.log('Authenticated URL:', page.url());
await page.screenshot({ path: 'account.png', fullPage: true });

For a single-page app that does not navigate, wait for a selector or state that only appears after successful authentication. If the signal does not appear, capture the final URL and relevant DOM for diagnosis instead of assuming a delay will fix it.

6. Inspect what Chrome actually reached

Compare the screenshot with the final URL and rendered DOM. Chrome’s --dump-dom outputs the DOM after parsing and script execution, which can reveal a redirect or login message hidden by the screenshot alone.

google-chrome --headless --disable-gpu \
  --timeout=10000 \
  --dump-dom 'https://example.com/account'

To capture a page from the command line, Chrome documents --screenshot and --window-size. This does not add authentication; the command still uses whatever browser state its invocation has available.

google-chrome --headless --disable-gpu \
  --window-size=1440,1000 \
  --timeout=10000 \
  --screenshot=page.png \
  'https://example.com/account'

Use the final URL, DOM, and session state together to distinguish a genuine authentication redirect from capture timing or an unexpected page load.

7. Check Chrome and Headless versions

When visible and automated runs behave differently, record the browser and automation versions and confirm which Headless implementation is running. Chrome’s new Headless mode runs Chrome itself. Chromium documents that the old Headless implementation ceased to be part of the Chrome binary as of M132; users who specifically need it should use chrome-headless-shell.

A version or implementation difference is worth investigating, but Headless mode by itself is not documented as discarding authentication. Verify the actual profile, storage, and page state before attributing the login page to Headless.

Choose an authentication approach

Approach When it fits Verify
Persistent profile (userDataDir) Controlled automation that can maintain a browser profile between runs Correct profile path, session validity, ownership, isolation, and site compatibility
Script the application login Repeatable tests using the supported login flow Redirect completion, MFA or consent requirements, and a reliable signed-in signal
Restore required cookies or storage Controlled test runs with safely obtained session state Correct origin and context, expiration, and any local storage or IndexedDB dependencies
Use Lighthouse’s authenticated workflow Auditing pages that require a login Whether storage is reset and whether the restored state remains current

Compare these choices by persistence, repeatability, site compatibility, security isolation, and how reliably you can verify readiness. No one approach works for every site’s authentication policy.

Or skip the browser setup

If you need a screenshot of a public page and do not need to automate a private login flow, ScreenshotNeo provides a website screenshot API and MCP server. See the API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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);

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

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

Troubleshooting checklist

Symptom Likely cause What to do
Login page appears only in automation Automation uses a fresh profile or isolated context Use the intended profile/context or establish authentication in that run
It worked on the prior run Session expired, was revoked, or storage was reset Check cookie expiry and tool storage settings; refresh the session through the supported flow
Screenshot shows login immediately after submit Capture occurred before redirect or app rendering completed Wait for navigation and a site-specific signed-in selector
page.authenticate() has no effect The page uses a web form or OAuth, not HTTP authentication Automate the application flow or provide the required application session state
Cookie appears set but access is denied Wrong origin/context, incomplete state, or site rejected the session Check cookie domain/path/expiry and other storage requirements; inspect redirects
CLI DOM or image differs by machine Different Chrome versions, Headless implementations, profiles, or timing Record versions and flags, compare final URLs and DOM, and check M132 Headless changes

Performance, reliability, and cost

  • Wait for a condition, not an arbitrary long delay. A specific navigation or authenticated-page signal avoids capturing too early while keeping runs from waiting longer than needed.
  • Use persistent state deliberately. It can avoid repeating login, but makes profile protection, ownership, cleanup, and concurrency important operational concerns.
  • Keep credentials out of source and logs. Use environment-based secret handling in automation and avoid logging cookie values or passwords.
  • Make failures observable. Record final URL, browser version, capture time, and a safe diagnostic of the page state so an auth redirect can be separated from a rendering delay.
  • Account for retries. A retry against an expired or rejected session will usually reproduce the login page; refresh or re-establish authentication before retrying the capture.
  • There is no universal timing or cost figure. Runtime and infrastructure cost depend on the browser, page, authentication flow, and environment. Measure your own run under representative conditions.

FAQ

Does Headless Chrome use my regular Chrome login?

Do not assume it does. Confirm the profile and context used by the automation and inspect their state.

Why does Lighthouse show a login page?

Its default run starts as a new user without prior session or storage data. Use its authenticated-page workflow when auditing a protected page.

Can Puppeteer reuse a logged-in session?

It can launch with a selected user data directory or work with browser/context cookies. The site’s session must still be valid and all required state must be present.

Does a screenshot command log in for me?

No. A screenshot records the page the browser reached; authentication must be established separately.