Puppeteer Screenshot with Cookies and Authentication
Set cookies or HTTP authentication before navigating, wait for an authenticated page state, and capture it with Puppeteer. Includes runnable code, screenshot options, and fixes for common errors.
To capture an authenticated page with Puppeteer, establish its session before navigating, wait for a page element that proves the user is signed in, then call page.screenshot(). Use browser cookies for cookie-based sessions; use page.authenticate() only for HTTP authentication such as Basic Auth. The steps below use Puppeteer’s current BrowserContext cookie API, which avoids deprecated page-level cookie methods.
1. Install Puppeteer
This example uses Node.js with Puppeteer’s bundled browser. In a new project:
npm init -y
npm install puppeteer
Save the main example as capture.mjs and run it with node capture.mjs. Replace the example URL, cookie data, and ready selector with values for your application. Puppeteer’s screenshot guide introduces the capture workflow.
2. Set a session cookie before navigation
Cookie injection suits applications where a valid session is stored in cookies and you already have the cookie data. Set it on the BrowserContext before opening the target page. The cookie’s scope and flags must match the site’s requirements; a session token alone may not be sufficient.
import puppeteer from 'puppeteer';
const targetUrl = 'https://example.com/account';
const browser = await puppeteer.launch({ headless: true });
const context = await browser.createBrowserContext();
try {
await context.setCookie({
name: 'session',
value: process.env.SESSION_COOKIE,
url: 'https://example.com/',
httpOnly: true,
secure: true,
sameSite: 'Lax',
});
const page = await context.newPage();
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
// Replace this with an element only visible to authenticated users.
await page.waitForSelector('[data-testid="account-home"]', {
visible: true,
timeout: 15000,
});
await page.screenshot({ path: 'account.png', fullPage: true });
} finally {
await browser.close();
}
Set the secret through the environment rather than putting a real credential in source code. For example, set SESSION_COOKIE in your shell or CI secret store before running the script. Never print cookie values in logs or commit them to fixtures.
Cookie scope and attributes
Use the cookie data that the target application actually issues. Puppeteer’s cookie API supports fields such as name, value, domain or URL, path, expiry, secure, httpOnly, sameSite, and partition key. See the official CookieData reference and BrowserContext.setCookie() reference.
- URL or domain: scope the cookie to the site that will receive it. A cookie for one host does not automatically apply to another.
- Path: a narrower path may prevent the target route from receiving the cookie.
- Secure: secure cookies are intended for HTTPS. Use the production scheme that matches the cookie.
- HttpOnly: this controls access from page JavaScript; it does not prevent the browser from sending the cookie.
- SameSite: choose the site’s required policy. Cross-site flows may need different settings than same-site navigation.
- Expiry: expired cookies will not authenticate. Session cookies can omit a persistent expiry.
Do not guess these properties when a capture fails. Compare them with the cookie attributes from the application’s own authentication flow, and confirm that the session is still valid.
3. Use HTTP authentication when the server requests it
For HTTP authentication, call page.authenticate() before navigating to the protected resource:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.authenticate({
username: process.env.HTTP_AUTH_USER,
password: process.env.HTTP_AUTH_PASSWORD,
});
await page.goto('https://protected.example.com/report', {
waitUntil: 'domcontentloaded',
});
await page.waitForSelector('main', { visible: true, timeout: 15000 });
await page.screenshot({ path: 'report.png', fullPage: true });
} finally {
await browser.close();
}
Page.authenticate() supplies credentials for HTTP authentication. It is not a general login-form, OAuth, MFA, or identity-provider automation method. Puppeteer documents that authentication enables request interception internally, which may affect performance. See Page.authenticate().
4. Choose the right session method
| Method | Use it when | Key consideration |
|---|---|---|
| Cookie injection | The app session is represented by cookies and valid cookie data is available. | Cookie scope, flags, expiry, and any related site state must be correct. |
page.authenticate() |
The server uses HTTP authentication. | It enables request interception internally and is not a form-login API. |
| BrowserContext per session | Captures or tests require isolated browser storage. | Establish the required cookies and other state inside each context. |
BrowserContexts isolate cookies and other storage, including local storage. Use a separate context when different test users or capture jobs must not share browser state. See the Puppeteer Page reference and the current API reference.
5. Wait for the right page state, then capture
Navigation completing does not prove that the application accepted the session or finished rendering its authenticated content. Wait for an application-specific signal, such as an account heading, a signed-in navigation element, or a test ID that only appears after authentication. A fixed delay can mask races and make every capture slower; network idle alone is not a universal guarantee that an app is ready.
Puppeteer’s core capture method is Page.screenshot(). Choose the output bounds deliberately:
// Current viewport
await page.screenshot({ path: 'viewport.png' });
// Whole document
await page.screenshot({ path: 'full-page.png', fullPage: true });
// A specific rectangle in CSS pixels
await page.screenshot({
path: 'region.png',
clip: { x: 0, y: 0, width: 900, height: 500 },
});
Screenshot options include path or returned bytes, output type, full-page capture, clipping, and transparent background behavior. An element handle can also capture one element and scroll it into view when needed. Refer to Page.screenshot(), ScreenshotOptions, and ElementHandle.screenshot().
Capture a single element
const card = await page.waitForSelector('[data-testid="account-summary"]', {
visible: true,
timeout: 15000,
});
if (!card) throw new Error('Account summary was not found');
await card.screenshot({ path: 'account-summary.png' });
6. Keep authenticated captures isolated and safe
- Create a fresh BrowserContext for each independent session when storage isolation matters.
- Close the browser in a
finallyblock so failures do not leave Chromium processes running. - Keep cookies and HTTP credentials in environment variables or your CI secret manager; do not expose them in logs, screenshots, or checked-in test data.
- Use test accounts and avoid capturing sensitive production data unless the task requires it and access is authorized.
- Expect sessions to expire or be bound to additional state. Cookie injection reproduces browser storage, but it does not bypass the application’s authentication rules.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its API documentation covers the request options. A basic one-call capture looks like this:
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);
These examples capture a URL as provided; they do not transfer your Puppeteer browser’s private cookies or credentials. ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for free screenshots.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The page redirects to sign-in | The cookie is expired, scoped to another host/path, or missing a required attribute or companion state. | Check the site-issued cookie data and set it before navigation. Confirm the authenticated signal after loading. |
waitForSelector times out |
The session was rejected, the selector differs, or the app has not rendered the expected view. | Inspect the final URL and page state; use a selector that reliably marks successful authentication, and set a timeout appropriate to the app. |
| HTTP credentials do not sign in | The site uses an HTML login form or another auth flow, not HTTP authentication. | Use the application’s supported test login flow or valid cookie session. page.authenticate() only addresses HTTP authentication. |
| The screenshot is blank or incomplete | Capture ran before the authenticated content rendered, or a full-page/viewport choice does not match the desired result. | Wait for a meaningful visible element and choose viewport, fullPage, clipping, or element capture intentionally. |
| The wrong account appears | Cookies or other storage were shared between runs. | Use a separate BrowserContext for each session and populate its state independently. |
| Capture is unexpectedly slow | The page itself is slow, readiness waits are too broad, or HTTP authentication’s interception adds overhead. | Wait on a specific ready signal, review navigation behavior, and measure whether HTTP authentication is needed for that resource. |
Performance, reliability, and cost notes
- Readiness controls speed and reliability: an application-specific selector avoids capturing too early and avoids waiting on unrelated background requests.
- Full-page images can be large: use viewport, clipping, or element capture when the whole document is unnecessary.
- Reuse browser processes thoughtfully: launching a browser for every image adds setup overhead; contexts can isolate sessions while sharing a browser process during a controlled run.
- Authentication can fail over time: cookies expire, account state changes, and identity flows may require more than a cookie. Treat auth as per-run input and report a clear failure if the expected signed-in element never appears.
- Cost: Puppeteer is an open-source browser automation library, but running it uses compute and browser resources in your environment. There is no screenshot API request charge in this local capture flow; account for your own machine or CI execution costs.
FAQ
Can Puppeteer take a screenshot without logging in through the UI?
Yes, if you can supply a valid session cookie or the site uses HTTP authentication. The correct method depends on how that site authenticates.
Are page.setCookie() methods still the right choice?
Use BrowserContext or Browser cookie methods in new code. The current Page reference marks page-level cookie methods deprecated.
Does setting a cookie guarantee access?
No. The session may be expired, incorrectly scoped, or dependent on additional application state. Confirm success by waiting for a signed-in page element.
Can Puppeteer capture only part of a page?
Yes. Use the screenshot clip option for a rectangle or an element handle’s screenshot method for a specific element.


