ScreenshotNeo

BlogHow-to

How to Take a Screenshot of a Password-Protected Web App with Selenium

Log in with Selenium, wait for authenticated content, and save a screenshot. Choose UI login or supported test-state setup based on what your test needs to cover.

By the ScreenshotNeo team4 October 20269 min read

To screenshot a password-protected web app with Selenium, first establish an authorized authenticated session, wait for a reliable marker that proves the protected page is ready, then save the screenshot. Use the login form when the test needs to cover login. For repeatable setup when login itself is not under test, use an application-supported API or test setup route to establish session state.

This guide uses Python and Selenium 4. Replace the example host, selectors, and credentials with values for an application and test account you are authorized to use. Selenium is a browser automation tool; the examples do not bypass authentication.

1. Choose how to establish the session

Approach Use it when Trade-off
Log in through the UI The login flow, form, or post-login redirect is part of the test. Exercises the real browser flow, but adds steps and depends on the login page and identity provider.
Set up state through an approved API or test mechanism The test is about the protected page, and repeating login would only be setup. Usually reduces repeated browser work, but depends on app-supported session setup and cookie rules.

Selenium’s guidance on [generating application state](https://www.selenium.dev/documentation/test_practices/encouraged/generating_application_state/) recommends avoiding repetitive browser actions for preparation when another supported mechanism can establish the state. If login behavior is what you are verifying, keep the UI login in the test.

2. Install Selenium and a browser

Install Selenium in the same Python environment that will run the script:

python -m pip install selenium

Have a supported browser available. Selenium’s driver management can generally obtain the matching driver when the environment permits it; locked-down or offline environments may require a browser and driver provisioned by your CI image. Store test credentials in environment variables or a secret manager, not source control.

3. Log in through the UI and capture the page

The key detail is the explicit wait for an authenticated-page marker. A successful navigation or a completed login click does not prove that a JavaScript-rendered dashboard is ready.

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

login_url = "https://app.example.test/login"
username = os.environ["TEST_USERNAME"]
password = os.environ["TEST_PASSWORD"]
output = Path("artifacts/protected-page.png")
output.parent.mkdir(parents=True, exist_ok=True)

driver = webdriver.Chrome()
try:
    driver.get(login_url)
    wait = WebDriverWait(driver, 20)

    wait.until(EC.visibility_of_element_located((By.NAME, "username"))).send_keys(username)
    driver.find_element(By.NAME, "password").send_keys(password)
    driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()

    # Choose a stable element visible only after successful authentication.
    wait.until(
        EC.visibility_of_element_located(
            (By.CSS_SELECTOR, "[data-testid='account-home']")
        )
    )

    if not driver.save_screenshot(str(output)):
        raise OSError(f"WebDriver could not save screenshot to {output}")
finally:
    driver.quit()

print(f"Saved {output}")

Set the credentials in the shell or CI secret configuration before running, for example:

export TEST_USERNAME='test-user'
export TEST_PASSWORD='test-password'
python capture.py

On PowerShell, use $env:TEST_USERNAME='test-user' and $env:TEST_PASSWORD='test-password'. Adapt the selectors to the actual app. Prefer stable attributes such as test IDs over styling classes that may change during redesigns.

If the application provides a supported test login API, use it to create a dedicated test session, then add the resulting session cookie to the WebDriver session. The exact API request, cookie name, value, domain, and security attributes are application-specific; there is no universal session-cookie recipe.

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

base_url = "https://app.example.test"
protected_url = f"{base_url}/protected"
session_cookie_value = os.environ["TEST_SESSION_COOKIE"]
output = Path("artifacts/protected-page.png")
output.parent.mkdir(parents=True, exist_ok=True)

driver = webdriver.Chrome()
try:
    # Selenium requires the browser to be on the cookie's domain first.
    driver.get(base_url + "/")
    driver.add_cookie({
        "name": "session",
        "value": session_cookie_value,
        "path": "/",
        # Set secure and sameSite to values compatible with your app if needed.
    })
    driver.get(protected_url)

    WebDriverWait(driver, 20).until(
        EC.visibility_of_element_located(
            (By.CSS_SELECTOR, "[data-testid='account-home']")
        )
    )
    if not driver.save_screenshot(str(output)):
        raise OSError(f"WebDriver could not save screenshot to {output}")
finally:
    driver.quit()

Read Selenium’s [cookie interaction guide](https://www.selenium.dev/documentation/webdriver/interactions/cookies/) for cookie behavior. Use this route only when the app’s approved setup process supports it. SSO, MFA, short-lived sessions, cookie domain/path restrictions, and identity-provider policies can make cookie setup unsuitable; validate against the actual application.

5. Wait for the right state

An explicit wait polls a specific condition until it succeeds or its timeout expires. Good conditions include a visible account heading, dashboard container, authenticated navigation item, or a URL/state that is specific to the logged-in view. Selenium advises: “Do not mix implicit and explicit waits.” See [Waiting Strategies](https://www.selenium.dev/documentation/en/webdriver/waits/).

  • Wait for a state that proves both authentication and content readiness.
  • For data loaded after initial render, wait for the particular table, chart, or status element you need in the image.
  • Use a fixed delay only when the app has a known delay with no observable condition; it is usually less reliable than waiting on the page state.
  • Set a reasonable timeout for the environment and fail with a useful error if the marker never appears.

Navigation readiness is separate from application readiness. Selenium’s [browser options](https://www.selenium.dev/documentation/webdriver/drivers/options/) describe page-load strategies: normal waits for document completion, eager for the interactive state, and none does not wait for readiness. JavaScript can still update content after navigation returns, so pair any strategy with an explicit application-level wait.

6. Screenshot behavior and useful options

Python’s save_screenshot(path) saves a PNG and returns a boolean indicating whether saving succeeded; see the [Python WebDriver API](https://www.selenium.dev/selenium/docs/api/py/selenium_webdriver_common/selenium.webdriver.common.webdriver.html). This captures the browser’s screenshot area, whose dimensions and behavior depend on the browser and driver context. Do not assume a portable full-page screenshot from this call. If the deliverable requires the entire page, verify the behavior for your chosen browser and driver or use a browser-specific supported approach, then inspect the resulting dimensions.

For a consistent viewport, set the window size before navigating or capturing:

driver.set_window_size(1440, 1000)

For a specific element, use the element screenshot API where supported by your Selenium/browser combination:

element = driver.find_element(By.CSS_SELECTOR, "[data-testid='report']")
element.screenshot("artifacts/report.png")

Element screenshots can still be affected by clipping, scroll position, sticky elements, and browser implementation. Check the output for the target browser rather than assuming identical pixels across platforms.

7. Other language and command-line options

The complete browser automation above is in Python. Selenium also provides a JavaScript WebDriver API, but a browser session must still be created and authenticated before capture. Its takeScreenshot() method returns base64-encoded PNG data according to the [JavaScript WebDriver API](https://www.selenium.dev/selenium/docs/api/javascript/WebDriver.html).

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

(async () => {
  const driver = await new Builder().forBrowser('chrome').build();
  try {
    await driver.get('https://app.example.test/login');
    await driver.findElement(By.name('username')).sendKeys(process.env.TEST_USERNAME);
    await driver.findElement(By.name('password')).sendKeys(process.env.TEST_PASSWORD);
    await driver.findElement(By.css("button[type='submit']")).click();
    await driver.wait(
      until.elementIsVisible(await driver.findElement(By.css("[data-testid='account-home']"))),
      20000
    );
    const png = await driver.takeScreenshot();
    await fs.mkdir('artifacts', { recursive: true });
    await fs.writeFile('artifacts/protected-page.png', png, 'base64');
  } finally {
    await driver.quit();
  }
})();

Install the Node package with npm install selenium-webdriver, and ensure the corresponding browser and driver are available. This is a browser automation workflow; cURL alone cannot perform the rendered-browser login and screenshot. If the app offers an authorized API session, cURL can call that app-specific API, but the request and returned cookie format cannot be generalized from Selenium’s documentation.

8. Troubleshooting

Symptom Likely cause Fix
Wait times out after submit Login failed, selector is wrong, redirect is delayed, or the expected element is not a stable authenticated marker. Inspect the current URL and page state; confirm credentials and selectors; wait for an element unique to the signed-in page.
Screenshot shows the login page Authentication did not complete, or the wait condition matched an element also present while logged out. Choose a marker exclusive to the protected state and confirm the session has not expired.
Cookie is rejected or user remains logged out WebDriver was not on the cookie domain first, or cookie scope, expiry, SameSite, Secure, or identity-provider rules do not match. Navigate to the app domain before adding it; obtain a fresh cookie through the approved test mechanism and match the app’s required attributes.
Screenshot is blank or content is missing Navigation returned before client-side rendering or asynchronous data loading finished. Wait for the specific content element, not just the document load event; check browser console and app errors if it never appears.
Output file is missing Parent directory does not exist, path is relative to an unexpected working directory, or save returned false. Create the directory, use a known path, inspect the boolean result, and ensure the process can write there.
Browser or driver fails to start Browser/driver mismatch, missing browser in the runtime, or restricted driver download. Provision compatible browser and driver versions in the environment and review Selenium startup errors.

9. Performance, reliability, and cost

For repeated captures, avoid re-running UI login when the test is not about login and an approved setup API exists. Reuse a browser session for a related batch only when session isolation and test independence remain clear; always close the driver in a finally block so failed tests do not leave browser processes behind. Use a dedicated test account, avoid production data, and do not print session cookies or passwords into CI logs.

Explicit waits make runtime depend on actual page readiness rather than a large fixed sleep, while a bounded timeout keeps a stalled page from hanging indefinitely. Under parallel execution, provision separate test identities or sessions if the application invalidates concurrent sessions. Selenium itself has no per-screenshot API charge in this workflow, but browser workers, CI minutes, and the app environment may have costs; the research sources provide no benchmark or cost figures.

Or skip the browser setup

If the page is publicly reachable or your authorized capture can be made accessible to the API through supported request headers or cookies, ScreenshotNeo can return an image with one GET request. The example below uses a public URL; it does not log into a protected app or bypass its authentication. For private pages, consult the [ScreenshotNeo documentation](https://screenshotneo.com/docs/) for supported request configuration and confirm your app’s authentication method is compatible.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://app.example.test/protected -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://app.example.test/protected"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://app.example.test/protected' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
  • Cookie banners, popups, and chat widgets are removed before the shot.
  • Bot checks, blank pages, and failed loads are never billed.
  • An MCP server lets AI agents use screenshot tools.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.

See [ScreenshotNeo](https://screenshotneo.com) and [create a free account](https://screenshotneo.com/account/sign-up/) to try 1,000 screenshots a month with no card.

FAQ

Should I automate MFA in the screenshot test?

Only if MFA is part of the behavior being tested and the application provides a safe, supported test path. Identity-provider rules vary, so the test setup must be specific to the app.

Use a dedicated authorized test account and the application’s approved session setup. Treat session cookies like passwords: keep them out of source control, screenshots, and logs.

Does a successful PNG save prove the screenshot is correct?

No. It confirms that WebDriver saved an image file. Validate that the expected authenticated content and dimensions are present when correctness matters.