How to Capture Screenshots of Websites with Basic Authentication
Capture HTTP Basic Authentication pages with Playwright, Puppeteer, DevTools, or a hosted API. Learn credential handling, errors, timing, and full-page shots.
Direct answer: if the site uses HTTP Basic Authentication, provide the username and password to the browser before navigation, then save the page screenshot. Playwright accepts credentials when you create a browser context; Puppeteer accepts them with page.authenticate(). A username-and-password form inside the page is a different application login and needs a form or session workflow instead.
Use an authorized target only. Keep credentials in environment variables or a secret manager, restrict access to captured files, and avoid putting passwords in URLs or source control.
1. Identify the authentication mechanism
HTTP Basic Authentication is a server challenge. The response includes WWW-Authenticate, and the browser answers with an Authorization header. Chromium documents Basic, Digest, NTLM, and Negotiate as HTTP authentication schemes with platform and policy qualifications: Chromium HTTP authentication.
A form such as “Email” and “Password” rendered by the site is application authentication. Browser context credentials do not automatically submit that form. Ask the site administrator which mechanism is enabled or inspect the initial response in the browser’s Network panel.
| What you see | Use |
|---|---|
Browser credential prompt or a 401 response with WWW-Authenticate |
HTTP credentials in Playwright or Puppeteer |
| Login page rendered as normal HTML | Automate the form, reuse an approved session, or use the application’s documented API |
| Need to see intermediate loading states manually | Chrome DevTools Network screenshot capture |
2. Playwright: repeatable HTTP Basic Authentication capture
Playwright’s network documentation shows httpCredentials on a browser context, and its Page API documents screenshot options: HTTP authentication and network and Page API.
Install
npm install playwright
npx playwright install chromium
Complete script
const { chromium } = require('playwright');
(async () => {
const username = process.env.SITE_USER;
const password = process.env.SITE_PASSWORD;
const target = process.env.SITE_URL || 'https://authorized.example/';
if (!username || !password) {
throw new Error('Set SITE_USER and SITE_PASSWORD');
}
const browser = await chromium.launch({ headless: true });
try {
const context = await browser.newContext({
httpCredentials: { username, password },
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1
});
const page = await context.newPage();
await page.goto(target, { waitUntil: 'networkidle', timeout: 60_000 });
await page.screenshot({ path: 'page.png', fullPage: true });
await context.close();
} finally {
await browser.close();
}
})();
SITE_USER='alice' SITE_PASSWORD='secret' SITE_URL='https://authorized.example/' node capture.js
Useful Playwright options
fullPage: truecaptures the document beyond the viewport. Omit it for a viewport screenshot.- Use
page.waitForSelector()for a known readiness element when network idle is not meaningful. - Use
page.waitForTimeout()only for a documented animation or delayed widget; prefer a state-based wait. - Set
viewportanddeviceScaleFactorexplicitly so output is reproducible. - For responsive testing, create separate contexts for each viewport rather than changing a page halfway through a capture.
3. Puppeteer: HTTP authentication with Chromium
Puppeteer documents page.authenticate(credentials) for HTTP authentication and page.screenshot() for saving images. Its API notes that authentication enables request interception behind the scenes, which can affect performance: Puppeteer Page API. Screenshot examples are in the Puppeteer screenshots guide.
Install
npm install puppeteer
Complete script
const puppeteer = require('puppeteer');
(async () => {
const username = process.env.SITE_USER;
const password = process.env.SITE_PASSWORD;
const target = process.env.SITE_URL || 'https://authorized.example/';
if (!username || !password) throw new Error('Set SITE_USER and SITE_PASSWORD');
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 1000, deviceScaleFactor: 1 });
await page.authenticate({ username, password });
await page.goto(target, { waitUntil: 'networkidle2', timeout: 60_000 });
await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
await browser.close();
}
})();
Because page.authenticate() uses request interception, measure your own job duration if you run many captures. Do not assume a timing from another browser library applies to your page.
4. Chrome DevTools: inspect screenshots during loading
For a one-off visual diagnosis, open DevTools, select the Network panel, open Network settings, enable Capture screenshots, and reload the page while the panel is focused. Thumbnail captures are associated with points in the loading timeline. The workflow is documented in Chrome DevTools: Inspect network activity.
This is useful for finding a flash of an error page, a late-loading hero image, or a layout shift. It is a manual timeline inspection, not a repeatable full-page artifact pipeline.
5. Capture from Python with Playwright
pip install playwright
playwright install chromium
import asyncio
import os
from playwright.async_api import async_playwright
async def main():
user = os.environ['SITE_USER']
password = os.environ['SITE_PASSWORD']
target = os.environ.get('SITE_URL', 'https://authorized.example/')
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context(
http_credentials={'username': user, 'password': password},
viewport={'width': 1440, 'height': 1000},
device_scale_factor=1,
)
page = await context.new_page()
await page.goto(target, wait_until='networkidle', timeout=60_000)
await page.screenshot(path='page.png', full_page=True)
await browser.close()
asyncio.run(main())
6. cURL: verify the protected response before opening a browser
cURL can confirm that the endpoint accepts HTTP Basic Authentication. It does not render HTML or create a visual screenshot.
curl --fail --silent --show-error \
--user "$SITE_USER:$SITE_PASSWORD" \
--dump-header response.headers \
--output page.html \
"$SITE_URL"
cat response.headers
Check for a successful status and the expected content. Never paste a real password into a shell history, CI log, or command shared with other users.
7. Hosted capture for recurring server-side jobs
A hosted API can remove browser installation and maintenance from a scheduled capture service. Capture documents an httpAuth parameter containing a Base64URL-encoded username:password: Capture authentication documentation. Base64URL is encoding, not encryption. Review the provider’s transport, storage, logging, retention, and authorization controls before sending protected credentials.
For a recurring workflow, decide whether the provider is approved to access the protected content, whether the page contains regulated or confidential data, and how long the resulting images may be retained.
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its API accepts one GET request and returns PNG, JPEG, WebP, or PDF. For an authorized protected page, configure the request’s custom Authorization header using the custom-header options in the ScreenshotNeo documentation; do not put credentials in the URL.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://authorized.example/ -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://authorized.example/"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://authorized.example/' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the page verdict and billing status with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. One thousand screenshots per month are free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
9. Timing, readiness, and full-page edge cases
- HTTP 401 after navigation: the credentials may be wrong, the realm may require another account, or the site may use a different scheme.
- Redirect to a login form: this is likely application authentication. Automate the approved login flow or load a pre-authenticated session.
- Lazy images missing: scroll through the page or wait for image elements before capturing. A full-page screenshot does not guarantee that every lazy resource has loaded.
- Infinite scroll: define a stopping condition; otherwise full-page capture can grow without bound.
- Cross-origin frames: you may capture the rendered frame, but DOM operations inside it can be restricted by browser security rules.
- Animations and rotating content: pause animations with approved test CSS or wait for a stable state to reduce visual drift.
- Very large pages: split captures by section or use a PDF/page-range workflow when one bitmap becomes impractical.
- Different environments: fonts, timezone, locale, geolocation, viewport, and device scale affect pixels. Set them explicitly for comparisons.
10. Troubleshooting checklist
| Symptom | Likely cause | Fix |
|---|---|---|
| Browser keeps prompting for credentials | Wrong username/password or unsupported challenge scheme | Inspect the WWW-Authenticate response and confirm the account and scheme with the administrator. |
| Screenshot is the login page | Form-based application login | Use the site’s documented login/session process; HTTP credentials only address HTTP authentication. |
| Navigation times out | Slow dependency, blocked resource, or never-ending network activity | Raise the timeout carefully, wait for a specific selector, and inspect failed requests. |
| Blank or partially rendered image | Capture occurred before the app rendered or assets loaded | Wait for a readiness element, fonts, and critical images; capture after the stable state. |
| Works locally but fails in CI | Missing browser binary, environment variables, fonts, proxy, or network access | Install the browser in the image, verify secrets are injected, and log status codes without logging credentials. |
| Unexpectedly slow Puppeteer jobs | Authentication request interception and page resources | Reuse a browser where appropriate, limit concurrency, and measure with the target site. |
| Credential appears in logs | Password embedded in URL or command line | Use environment variables or a secret manager and redact headers and URLs in logs. |
11. Performance, reliability, and cost
Performance
- Launch the browser once and reuse it for independent contexts when isolation allows.
- Limit concurrent pages to what the target and runner can sustain; excessive parallelism increases failures.
- Block nonessential resources only when doing so cannot change the page you need to document.
- Use selector-based readiness instead of an arbitrary long sleep.
Reliability
- Record URL, viewport, commit or build identifier, timestamp, HTTP status, and a hash of the output.
- Retry transient navigation failures with a bounded count and backoff; do not blindly retry authentication failures.
- Keep the browser, Playwright/Puppeteer version, fonts, locale, timezone, and viewport stable for visual regression work.
- Store artifacts with restricted access and a retention period appropriate to the protected content.
Cost
Local Playwright, Puppeteer, and DevTools have no per-shot API charge, but you operate browser compute, storage, CI time, and maintenance. A hosted service adds usage pricing and a third-party credential boundary. ScreenshotNeo bills only clean shots; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Its plans are Free: 1,000 shots/month; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan.
12. Security checklist
- Capture only systems and data you are authorized to access.
- Use a dedicated least-privilege account when possible.
- Pass secrets through environment variables or a secret manager.
- Do not use
https://user:password@host/in URLs that may be logged. - Redact
Authorizationheaders, cookies, and query strings from diagnostic logs. - Restrict screenshot artifacts and delete them according to your retention policy.
- Review whether a hosted provider may access the page and credentials before production use.
FAQ
Does Basic Authentication mean every password form works with these examples?
No. These examples target HTTP authentication challenges. A normal HTML login form requires its own workflow.
Can I use a screenshot tool without exposing the password?
A local browser keeps credentials in your environment. A hosted API requires a provider-level review of how credentials are transported, logged, stored, and accessed.
Should I capture the viewport or the whole page?
Use a viewport capture for what a user currently sees. Use full-page capture for documentation or review of the complete document, after handling lazy loading and unbounded content.
Why is the image different between runs?
Fonts, animations, ads, data, time, locale, timezone, viewport, and resource timing can change pixels. Fix those inputs and wait for a stable page state.
Can cURL create the screenshot?
No. cURL is useful for validating the HTTP response and credentials; a browser renderer such as Playwright or Puppeteer is needed for a visual screenshot.


