ScreenshotNeo

BlogHow-to

How to Capture Screenshots of Web Pages with Basic Authentication in Selenium

Use Selenium WebDriver BiDi to authenticate before navigation, verify the protected page, and save a PNG. Includes setup, troubleshooting, and a browser-free API option.

By the ScreenshotNeo team4 October 202610 min read

To capture a page protected by HTTP Basic Authentication, register a Selenium WebDriver BiDi authentication handler before navigating to the URL. After navigation, verify that the expected authenticated content is present, then save the current browser window as a PNG. The examples below use Selenium’s Python binding; the handler API and BiDi support vary by binding and browser. See Selenium’s BiDi network authentication documentation and BiDi setup guide.

1. Install Selenium and prepare the browser

Use a current Selenium 4 release and a browser and driver combination that supports the BiDi features used by your binding. Selenium Manager can manage drivers for many local setups. For remote browsers, configure the remote session to expose BiDi and use the browser vendor’s current instructions.

python -m pip install --upgrade selenium

BiDi must be enabled in browser options. In Python, set options.enable_bidi = True. Selenium’s support is evolving, so check the documentation for your exact Selenium version and browser if the option or network namespace is unavailable.

2. Capture an authenticated page with Python

This example uses the public Basic Auth demonstration page documented by Selenium. Replace its URL and credentials with values for your own authorized test environment. It verifies a known success paragraph before saving the screenshot, checks the save result, and removes the authentication handler during cleanup.

import os
from pathlib import Path

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

url = "https://the-internet.herokuapp.com/basic_auth"
username = os.environ["BASIC_AUTH_USERNAME"]
password = os.environ["BASIC_AUTH_PASSWORD"]
output = Path("artifacts/basic-auth.png")
output.parent.mkdir(parents=True, exist_ok=True)

options = webdriver.FirefoxOptions()
options.enable_bidi = True

driver = webdriver.Firefox(options=options)
handler_id = None

try:
    handler_id = driver.network.add_auth_handler(username, password)
    driver.get(url)

    expected = "Congratulations! You must have the proper credentials."
    WebDriverWait(driver, 15).until(
        EC.text_to_be_present_in_element((By.TAG_NAME, "p"), expected)
    )

    if not driver.save_screenshot(str(output)):
        raise OSError(f"Selenium could not write screenshot: {output}")
    print(f"Saved {output.resolve()}")
finally:
    if handler_id is not None:
        driver.network.remove_auth_handler(handler_id)
    driver.quit()

Supply credentials through your shell, CI secret store, or another appropriate secret mechanism. Do not commit real credentials to source control. For example, set BASIC_AUTH_USERNAME and BASIC_AUTH_PASSWORD in the environment before running the script. The Python BiDi example in Selenium’s docs follows the same key sequence: add the handler, navigate, verify the success text, and remove the handler in cleanup.

Use the browser you run in production

The sample selects Firefox because Selenium’s current Python BiDi network examples illustrate this workflow with Firefox. For Chrome or another browser, create that browser’s options and driver instead, enable BiDi as required, and confirm that your installed Selenium binding exposes driver.network.add_auth_handler. BiDi is Selenium’s standards-based bidirectional protocol; Selenium describes it as the cross-browser replacement for CDP. Actual feature availability can still depend on the binding and browser version.

3. What the code does and what to configure

Step or setting Purpose What to check
Enable BiDi Opens the bidirectional connection required for network events. Set the capability before creating the driver; confirm the browser/driver supports it.
Add the auth handler Responds to a browser authentication challenge with the supplied username and password. Register it before get() and keep it scoped to the capture’s lifetime.
Navigate Requests the protected resource so the browser can receive the server’s challenge. Use the exact scheme, host, port, and path for the authorized target.
Wait and verify Distinguishes authenticated content from an error, login prompt, or incomplete render. Wait for a stable page-specific element or text, not just navigation completion.
Save PNG Writes a screenshot of the current browser window. Ensure the destination directory exists and is writable; check the Boolean return value.
Remove handler and quit Limits credential handler lifetime and releases browser resources. Use finally so cleanup runs after failures too.

The username and password are passed to the handler as strings. If a site uses Digest authentication, Selenium also describes authentication handlers for that flow. This method addresses HTTP authentication challenges; it is not a substitute for automating a form-based login, an OAuth flow, or a single sign-on page.

4. Screenshot scope and output options

Python’s driver.save_screenshot(path) saves the current window as PNG. It returns False if an I/O error prevents saving. get_screenshot_as_png() returns PNG bytes, while get_screenshot_as_base64() returns a Base64-encoded string. Selenium documents these as current-window screenshots; do not assume the basic save call captures the whole document vertically. See the Python WebDriver screenshot API.

# Save to a file and check success
ok = driver.save_screenshot("artifacts/current-window.png")
if not ok:
    raise OSError("Screenshot could not be written")

# Or get the image in memory
png_bytes = driver.get_screenshot_as_png()

# Or get a Base64 representation
png_base64 = driver.get_screenshot_as_base64()

For a specific region, locate a WebElement and use its screenshot method, such as element.screenshot("element.png"). For full-page capture, check the API and browser behavior for the binding you use. Selenium’s JavaScript reference describes its screenshot selection as best effort among the whole page, current window, and visible frame; that is not a universal full-page guarantee across languages.

Set the viewport before navigation if responsive layout matters, using the binding’s window-size API. Page zoom, device scale factor, fonts, animations, and browser version can affect the output. Wait for the content you need and, where relevant, for images or client-side rendering to finish before capture.

5. BiDi versus CDP and other authentication approaches

Prefer the BiDi authentication handler when the selected language binding and browser support it. Selenium calls BiDi the W3C standard bidirectional protocol and its cross-browser replacement for Chrome DevTools Protocol. Selenium’s CDP documentation says CDP support is temporary while BiDi is implemented, and CDP APIs can vary with Chrome and DevTools versions. If an existing project uses CDP authentication registration, treat it as browser- and version-dependent and confirm the current Selenium docs before relying on it.

A URL containing credentials, such as https://user:password@example.test/, is not the recommended workflow here: it can expose secrets in logs, browser history, diagnostics, or tooling, and browser handling varies. Registering a handler before navigation keeps the flow explicit and avoids embedding the credential in the requested URL. This security guidance is practical advice; Selenium’s current BiDi example demonstrates the handler workflow.

6. cURL, Python, and Node.js alternatives

cURL for checking the HTTP endpoint

cURL can request an HTTP Basic Auth resource directly and is useful for checking whether the credentials and endpoint work. It does not render the page or produce a browser screenshot, so it is not a replacement for Selenium when you need the rendered page.

curl --user "$BASIC_AUTH_USERNAME:$BASIC_AUTH_PASSWORD" \
  --fail --show-error \
  "https://example.test/protected"

Use environment variables or a protected credential prompt/configuration rather than putting real credentials directly into a command that may be saved in shell history. The command’s output is the HTTP response body, not an image.

Python direct HTTP request

Likewise, Python’s requests library can verify an endpoint response but does not execute browser JavaScript or capture a rendered screenshot.

import os
import requests

response = requests.get(
    "https://example.test/protected",
    auth=(os.environ["BASIC_AUTH_USERNAME"], os.environ["BASIC_AUTH_PASSWORD"]),
    timeout=30,
)
response.raise_for_status()
print(response.status_code, response.headers.get("content-type"))
print(response.text[:500])

Node.js with Selenium

The Selenium authentication-handler signatures differ across language bindings and are version-sensitive. Use the Node binding’s current BiDi network authentication API and enable BiDi for the browser session before using it; do not copy the Python method signature blindly. Selenium’s official network page includes examples for multiple bindings. The browser screenshot itself is exposed in Selenium JavaScript as driver.takeScreenshot(), which returns Base64 PNG data:

const { Builder } = require('selenium-webdriver');
const fs = require('node:fs/promises');
const chrome = require('selenium-webdriver/chrome');

const options = new chrome.Options();
options.enableBidi();
const driver = await new Builder()
  .forBrowser('chrome')
  .setChromeOptions(options)
  .build();

try {
  // Register the Basic Auth handler using the API for your installed
  // selenium-webdriver version before navigating to the protected URL.
  await driver.get('https://example.test/protected');
  const pngBase64 = await driver.takeScreenshot();
  await fs.writeFile('protected.png', pngBase64, 'base64');
} finally {
  await driver.quit();
}

The code above shows the screenshot and session lifecycle; the auth registration is intentionally described rather than given a guessed method signature. Check the current Node binding reference and Selenium’s BiDi network examples for the exact version you install. For a reusable complete authentication example with a supported binding, use the Python workflow above.

7. Or skip the browser setup

If you need a screenshot file without running and maintaining a Selenium browser session, ScreenshotNeo is a website screenshot API. One request takes a URL and returns an image; see the ScreenshotNeo API documentation for parameters and response details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server gives AI agents tools to take screenshots. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. For an authenticated page, consult the API documentation for supported request options and do not assume that a Selenium browser’s existing session or credentials transfer to the API.

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

8. Troubleshooting

Symptom Likely cause Fix
driver.network or add_auth_handler is missing BiDi is not enabled, the Selenium version is old, or the browser/binding does not expose that feature. Upgrade Selenium, enable BiDi in options before driver creation, and verify support for the exact browser and binding.
Browser shows an auth prompt or an unauthorized page Handler was registered too late, credentials are wrong, or the endpoint uses a different authentication scheme or realm. Register before navigation; check credentials, URL and scheme; inspect the page response and use an API appropriate to the server’s auth scheme.
Screenshot contains an error page Navigation completed, but authentication or application loading did not succeed. Wait for an authenticated page element and fail the capture if it never appears. Do not treat successful navigation alone as success.
Wait times out despite correct credentials The success selector/text differs, the page is still rendering, or the site redirects after authentication. Inspect the final URL and page content; select a stable element unique to the expected page and adjust the wait to the application’s actual load behavior.
Screenshot file is missing or empty Destination path is invalid, parent directory is absent, or the process lacks write access. Create the directory, use an absolute or known writable path, check save_screenshot‘s Boolean result, and confirm the file after writing.
Image shows only part of a long page The basic Python method captures the current window rather than promising full-page output. Use a documented full-page method supported by your chosen binding/browser, or capture a specific element if that meets the need.
BiDi session fails to start Browser, driver, or Selenium versions are incompatible, or the remote endpoint does not expose the required WebSocket connection. Align browser and driver versions, review the BiDi setup guidance, and confirm remote-session capabilities.
Credentials appear in logs Secrets were embedded in source, a URL, or verbose diagnostics. Load them from a secret store or environment, avoid logging them, and rotate any credential that was exposed.

9. Performance, reliability, and cost

  • Performance: Browser startup is usually a larger fixed part of a one-off capture than saving the PNG. Reuse a driver for a controlled batch of pages when isolation requirements permit, and always close it. Set finite page-load and explicit-wait timeouts so a stalled site does not occupy a worker indefinitely.
  • Reliability: Capture only after checking page-specific authenticated content. Use deterministic viewport settings and stable selectors. Keep handler cleanup and driver shutdown in finally; for parallel work, use isolated browser sessions so handlers, cookies, and windows do not leak across jobs.
  • Cost: Selenium itself is an open-source browser automation framework, but running browsers has infrastructure costs: compute, memory, storage, and any hosted browser service you choose. The Selenium docs reviewed provide no topic-specific cost or performance figures, so avoid treating any fixed runtime or price as guaranteed.
  • Security: Use least-privilege test credentials, limit where they are valid, do not publish screenshots containing private data, and avoid retaining credentials or captures longer than necessary.

10. FAQ

Can Selenium handle Basic Auth without opening a prompt?

Yes, when the selected binding and browser support the BiDi authentication handler. Install it before navigating so it can answer the challenge.

Does the screenshot include the username and password?

The handler supplies credentials to the authentication flow; it does not mean the credentials should be visible in the captured page. The screenshot records rendered browser content, so check the page itself for any sensitive information before sharing it.

Will this work for a login form?

No. HTTP Basic Auth is a browser/network authentication challenge. A username and password form is an application login flow and requires interacting with that page’s controls or an appropriate test authentication mechanism.

Does the Python method create a full-page screenshot?

It saves the current window as PNG. Full-page behavior is binding- and browser-specific; consult the API for the implementation you use.

Can the screenshot be JPEG or WebP?

The documented Python WebDriver save method writes PNG. Convert the resulting image separately if another format is required.

Sources