How to Authenticate with Puppeteer for Pages Behind Login
Choose the right Puppeteer authentication method for HTTP auth, login forms, session cookies, or headers—and verify the page is actually signed in.
Use the authentication method the site actually expects: page.authenticate() for HTTP authentication, browser interactions for an HTML login form, cookies to restore an existing session, or extra request headers for a service that documents header-based access. Then verify a site-specific signed-in indicator or protected content; a completed navigation alone does not prove login succeeded.
This guide covers authorized access to pages you are permitted to use. Login selectors, multi-factor authentication (MFA), consent steps, and success indicators vary by site, so form-login code below is a pattern to adapt rather than a universal recipe.
1. Identify the login mechanism
| What the site uses | Puppeteer approach | Scope and caveat |
|---|---|---|
| HTTP authentication challenge | page.authenticate({ username, password }) |
Credentials answer HTTP authentication. Puppeteer enables request interception internally, which may affect performance. |
| HTML login page | Navigate, fill the form, submit, and check a signed-in state | Selectors, MFA, consent, and success checks are site-specific. |
| Existing browser session | Set valid cookies in the browser or browser context before navigation | Cookies are sensitive and must match the site’s domain, attributes, and session validity. |
| Documented token or custom header | page.setExtraHTTPHeaders() |
Configured headers are sent with every request the page initiates. Use only when the service expects this mechanism. |
Do not use page.authenticate() as if it submits a website’s HTML login form. It is specifically for HTTP authentication. The current API reference also cautions that request interception is turned on behind the scenes and might affect performance: Puppeteer Page.authenticate().
2. Set up Puppeteer
Install Puppeteer in a Node.js project. The package normally downloads a compatible browser during installation; follow the official installation guidance if your environment manages browsers separately.
npm install puppeteer
Save credentials outside source control. The examples read them from environment variables. Do not log passwords, session cookies, or authorization tokens.
3. HTTP authentication with page.authenticate()
Call authenticate() before navigating to the protected resource. Puppeteer’s credentials contain string properties named username and password. Passing null disables HTTP authentication.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
const username = process.env.HTTP_AUTH_USERNAME;
const password = process.env.HTTP_AUTH_PASSWORD;
if (!username || !password) {
throw new Error('Set HTTP_AUTH_USERNAME and HTTP_AUTH_PASSWORD');
}
await page.authenticate({ username, password });
const response = await page.goto('https://example.com/protected', {
waitUntil: 'domcontentloaded',
timeout: 30000,
});
console.log('HTTP status:', response?.status());
// Replace this with a site-specific check for protected content.
const pageTitle = await page.title();
console.log('Title:', pageTitle);
// If you need to disable HTTP authentication later:
// await page.authenticate(null);
} finally {
await browser.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
An HTTP response with status 404 or 503 is still a completed request. Check the response status and the page’s expected authenticated content instead of treating navigation completion as proof of access. Redirects cause subsequent requests, so validate the final page too. See Puppeteer HTTPRequest.
4. Log in through an HTML form
For a normal web application login page, use the page’s form. The following runnable pattern assumes environment variables for the URL, selectors, and credentials. Set the selectors to match the target site and replace the success check with an indicator that only appears for a signed-in user.
const puppeteer = require('puppeteer');
(async () => {
const loginUrl = process.env.LOGIN_URL;
const username = process.env.LOGIN_USERNAME;
const password = process.env.LOGIN_PASSWORD;
const usernameSelector = process.env.USERNAME_SELECTOR;
const passwordSelector = process.env.PASSWORD_SELECTOR;
const submitSelector = process.env.SUBMIT_SELECTOR;
const signedInSelector = process.env.SIGNED_IN_SELECTOR;
if (![loginUrl, username, password, usernameSelector, passwordSelector,
submitSelector, signedInSelector].every(Boolean)) {
throw new Error('Set the login URL, credentials, and all site-specific selectors');
}
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto(loginUrl, { waitUntil: 'domcontentloaded', timeout: 30000 });
await page.waitForSelector(usernameSelector, { visible: true, timeout: 10000 });
await page.locator(usernameSelector).fill(username);
await page.locator(passwordSelector).fill(password);
// A click can navigate, update the page in place, or reveal an MFA step.
await page.locator(submitSelector).click();
// This is the actual success check; choose a site-specific signed-in element.
await page.waitForSelector(signedInSelector, { visible: true, timeout: 15000 });
console.log('Signed-in indicator found.');
} finally {
await browser.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
The locator fill() method is shown in the current Puppeteer API. If the site uses an iframe, shadow DOM, a multi-step form, or an MFA challenge, adapt the interaction to that page. Do not assume that clicking submit or waiting for navigation is sufficient: the site may reject credentials, remain on the login page, or require another step. Official locator guidance: Puppeteer page interactions.
5. Restore an existing session with cookies
If you already have a valid session cookie obtained through an authorized flow, set it on the browser context before visiting the protected page. Use the current browser-context cookie API rather than the deprecated page-level cookie methods.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const context = await browser.createBrowserContext();
const page = await context.newPage();
const sessionValue = process.env.SESSION_COOKIE_VALUE;
if (!sessionValue) throw new Error('Set SESSION_COOKIE_VALUE');
await context.setCookie({
name: 'session', // Replace with the cookie name expected by the site.
value: sessionValue,
domain: 'example.com', // Match the target site's cookie domain.
path: '/',
secure: true,
httpOnly: true,
sameSite: 'Lax', // Use the value required by the site, if known.
});
await page.goto('https://example.com/account', {
waitUntil: 'domcontentloaded',
timeout: 30000,
});
// Replace with an account-only element or protected content check.
await page.waitForSelector('[data-account-menu]', {
visible: true,
timeout: 10000,
});
console.log('Session appears active.');
} finally {
await browser.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
The cookie name and value above are examples; use the exact attributes the application requires. A cookie may be expired, scoped to another domain or path, restricted by security attributes, or insufficient without related cookies. Treat session cookies as credentials: keep them out of code, logs, and shared artifacts. Puppeteer documents browser cookie operations in its cookie guide.
6. Use headers only when the service expects them
For an endpoint that documents header-based authentication, set the required headers before navigation. Puppeteer sends the configured headers with every request initiated by the page. Header names are lowercased and their order is not guaranteed.
const puppeteer = require('puppeteer');
(async () => {
const token = process.env.ACCESS_TOKEN;
if (!token) throw new Error('Set ACCESS_TOKEN');
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
authorization: `Bearer ${token}`,
});
await page.goto('https://example.com/protected', {
waitUntil: 'domcontentloaded',
timeout: 30000,
});
// Verify a site-specific authenticated state or protected response.
console.log('Final URL:', page.url());
} finally {
await browser.close();
}
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Because the header applies to all page requests, use it only for a page and destination where the service expects it. Avoid navigating to unrelated origins with a sensitive header configured. See Puppeteer Page.setExtraHTTPHeaders().
7. Verify that authentication worked
After any method, check evidence tied to the target application. Useful checks include:
- A known account or sign-out element is visible.
- The final URL is an expected authenticated destination.
- Protected content that is absent when logged out is present.
- The response status and page content are consistent with success.
Use more than a successful goto(). HTTP error responses can still complete normally, redirects issue another request, and some applications update the page without a full navigation. A site-specific indicator is the meaningful assertion.
8. Troubleshooting
| Symptom | Likely cause | What to check or change |
|---|---|---|
page.authenticate() does not log into the page |
The site has an HTML form rather than an HTTP authentication challenge. | Inspect the login flow and use form interaction, a valid session cookie, or the documented auth mechanism. |
| 401 response or repeated credential prompt | Wrong credentials, unsupported credential scheme, or the protected request was made before authentication was configured. | Confirm the server’s HTTP auth mechanism and credentials; call authenticate() before navigation. |
| Login click succeeds but page remains signed out | Invalid credentials, a validation message, MFA, consent, or an unhandled form step. | Inspect visible page state and handle the site’s next required step; wait for a signed-in indicator. |
| Timeout waiting for a selector | Selector mismatch, element in a frame, hidden element, or login flow changed. | Inspect the authorized page, update selectors, check visibility and frame context, and use a timeout appropriate to the site. |
| Cookie is set but access is still denied | Expired or incomplete session, wrong domain/path, or missing required cookie attributes. | Use a currently valid cookie set for the correct domain and context, and verify whether the site requires additional session state. |
| Token header appears ineffective | The endpoint expects another scheme/header, or the protected content is loaded from a different request context. | Follow the service’s documented header contract and verify the actual protected response. Remember the configured header applies across page requests. |
| Navigation finishes on an error page | HTTP 404/503 or an application-level error can still be a completed request. | Inspect response status, final URL, and protected content; do not equate completion with authentication. |
| Automation is slower after HTTP auth setup | page.authenticate() enables request interception internally. |
Use it only for HTTP auth and account for the documented performance caveat; avoid assuming the same setup cost applies to other methods. |
9. Performance, reliability, and cost
- HTTP authentication: Puppeteer documents that
page.authenticate()turns on request interception behind the scenes and may affect performance. Measure in your own workload if latency matters. - Waits: Wait for the specific result you need, such as a visible signed-in element, rather than relying on a generic navigation event. Keep finite timeouts so failed login flows do not hang indefinitely.
- Reliability: Site selectors and authentication rules can change. Keep the success assertion explicit, handle errors, and close the browser in a
finallyblock. - Secrets: Store credentials, tokens, and session cookies in an appropriate secret store or environment configuration. Avoid printing them or persisting them in source control.
- Cost: Puppeteer itself is a browser automation library; runtime cost depends on the infrastructure and browser workload you operate. This guide does not claim a benchmark or fixed hosting price.
10. Or skip the browser setup
If your goal is a screenshot rather than browser automation, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for parameters and options.
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}`);
ScreenshotNeo accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, with response headers showing the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up free for 1,000 screenshots a month, with no card required.
11. Frequently asked questions
Can Puppeteer bypass a login or MFA challenge?
This guide covers authorized authentication flows. MFA and other additional checks are controlled by the target site; use its supported, authorized process and handle any required step explicitly.
Should I reuse a browser context between accounts?
Keep each account’s cookies and storage isolated in its own browser context when the workflows must remain separate. A context is where browser storage such as cookies is managed.
Does a successful HTTP response prove I am logged in?
No. Check the expected account state or protected content as well as the response and final URL.
Can I turn off HTTP authentication?
Yes. Puppeteer’s API accepts null to disable credentials previously configured with page.authenticate().


