ScreenshotNeo

BlogHow-to

How to Test Pages Behind Basic Authentication with BackstopJS

Configure BackstopJS to capture HTTP Basic-authenticated pages with Puppeteer, handle readiness and visual comparisons, and troubleshoot common failures.

By the ScreenshotNeo team4 October 20267 min read

To test a page protected by HTTP Basic authentication with BackstopJS, use its Puppeteer engine and an onBeforeScript hook that calls page.authenticate({ username, password }) before the scenario navigates to the page. Keep credentials in environment variables or your CI secret store. The example below combines documented BackstopJS and Puppeteer APIs; it is a setup example, not code reported as tested.

Configure HTTP Basic authentication

HTTP Basic authentication is handled by the browser at the HTTP request layer. Puppeteer’s Page.authenticate() supplies the credentials for that challenge. BackstopJS exposes the Puppeteer page to its per-scenario onBeforeScript hook, which is intended for browser setup before a scenario runs.

1. Add a scenario and hook

In backstop.json, select the Puppeteer engine, point to the hook, and set a protected URL. Use a readiness condition that identifies content available only after authentication:

{
  "engine": "puppeteer",
  "onBeforeScript": "auth.js",
  "scenarios": [
    {
      "label": "Protected account page",
      "url": "https://staging.example.test/protected",
      "readySelector": "main"
    }
  ]
}

2. Create the authentication hook

With the default engine-script path, create backstop_data/engine_scripts/auth.js. If your configuration uses a custom paths.engine_scripts, put the script in that directory instead.

module.exports = async (page) => {
  const username = process.env.BASIC_AUTH_USER;
  const password = process.env.BASIC_AUTH_PASSWORD;

  if (!username || !password) {
    throw new Error('Set BASIC_AUTH_USER and BASIC_AUTH_PASSWORD');
  }

  await page.authenticate({ username, password });
};

BackstopJS documents the fuller hook signature as onBefore(page, scenario, viewport, isReference, Engine, config); this example uses only the page argument. The hook runs for each scenario, so it can set up the page before navigation. A scenario can override the root hook if different scenarios need different setup. Check the configuration against your installed BackstopJS version, especially if it is older or uses a custom engine.

3. Set credentials outside the repository

For a local shell, export credentials before running BackstopJS:

export BASIC_AUTH_USER='your-test-user'
export BASIC_AUTH_PASSWORD='your-secret-password'
npx backstop test

In CI, add both values to the CI platform’s secret store and expose them as environment variables to the BackstopJS job. Do not put real credentials in backstop.json, the hook, source control, or logged command arguments.

4. Capture and compare

BackstopJS uses reference screenshots and test screenshots for visual comparison. Capture a reference when setting up the scenario, run the test after changes, and review the visual report before approving updated references. Approving a changed reference updates the baseline used by later comparisons, so confirm that the authenticated content and capture region are correct first.

Choose the right readiness and capture region

Authentication can succeed while the application still needs time to render. Use a condition tied to the authenticated content whenever possible. A selector such as main is only useful if it reliably appears after the protected page is ready; a generic container that exists on a login or error page can give a misleading success.

  • readySelector: wait for a specific element, such as an account heading or protected panel.
  • readyEvent: wait for an application event when the page exposes a dependable readiness signal.
  • Delay: use a fixed delay only when there is no observable readiness condition. It may make runs slower and still be too short under load.

Choose the screenshot region to match the visual behavior under test. BackstopJS scenarios can capture the document, the viewport, or explicit CSS selectors. Use a full-page/document capture for content extending below the fold, a viewport capture for above-the-fold layout, or a selector when only one component matters. Keep the region consistent between reference and test runs.

HTTP Basic authentication versus a login form

page.authenticate() is for HTTP authentication challenges. It does not fill in a username and password form rendered inside a page. For a form-based login, use a deliberate browser interaction or restore session state using the mechanisms supported by your chosen engine.

BackstopJS supports both Puppeteer and Playwright, and its current README identifies Puppeteer as the default. If you switch to its Playwright integration, use the Playwright engine and its corresponding scripts. BackstopJS documents Playwright storageState for loading cookies and localStorage before tests; that is useful for browser session state, but it is not a documented substitute for supplying HTTP Basic credentials.

Configuration and operational notes

Setting or choice Use Watch for
engine Use puppeteer for the example above; BackstopJS also offers a Playwright integration. Engine-specific hooks and scripts are not interchangeable by assumption.
onBeforeScript Run browser setup, including Basic-auth credentials, before each scenario. Check custom engine-script paths and per-scenario overrides.
paths.engine_scripts Set the directory where custom engine scripts are stored. The hook file must be in the configured location.
readySelector, readyEvent, delay Wait for protected content to be ready before capture. A selector present on an error or login page can mask failed authentication.
Capture selector or page region Limit the screenshot to the document, viewport, or a chosen element. Reference and test must represent the same intended region.
Scenario hook override Apply different setup to scenarios that need it. Ensure the scenario still receives the required authentication setup.

Puppeteer notes that authentication turns on request interception behind the scenes, which might affect performance. Keep the hook focused, avoid unnecessary setup work on every scenario, and use the narrowest capture region that still covers the behavior you want to compare. BackstopJS runs browser captures, so reliability also depends on the protected service being reachable and the page reaching its ready condition consistently. For CI, provision credentials as secrets and make readiness reflect the actual authenticated page.

Troubleshooting

Symptom Likely cause Fix
Browser authentication prompt, 401 response, or unauthorized content Credentials are missing, wrong, or applied after navigation. Confirm both environment variables are present in the BackstopJS process, check the credentials, and ensure the hook is configured to run before the scenario visits the protected URL.
Error says to set BASIC_AUTH_USER and BASIC_AUTH_PASSWORD The hook cannot read one or both environment variables. Export both locally or configure both in the CI job environment. Check secret-to-environment-variable mapping without printing secret values.
Hook file is not found or does not run The script path or engine configuration does not match the installed BackstopJS setup. Check onBeforeScript, paths.engine_scripts, the selected engine, and the installed version’s configuration documentation.
Capture succeeds but shows a login page or access-denied page The target may use form-based login, credentials may not be accepted, or the readiness selector may also exist on the failure page. Inspect the actual destination and response state. Use browser interactions for a form login, or choose a selector unique to authenticated content.
Capture occurs before protected content finishes rendering The readiness condition is too broad or the application is asynchronous. Wait for a protected-content selector or application readiness event; use a delay only if no observable condition is available.
Visual diffs include unrelated content or omit expected content The capture region differs from the intended test, or the page state is not stable. Choose document, viewport, or explicit selectors deliberately; align the region in reference and test scenarios and review the report before approving baselines.
Runs become slower after enabling authentication Puppeteer authentication enables request interception behind the scenes. Keep setup small, avoid extra interception work, and limit capture scope where practical.

Or skip the browser setup

If you need a screenshot of a public page rather than a BackstopJS visual-regression baseline, ScreenshotNeo returns an image or PDF from one API request. The examples below use the documented API; see the ScreenshotNeo API documentation for options.

cURL

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

Python

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)

Node.js

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 import('node:fs/promises').then(({ writeFile }) =>
  writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);

ScreenshotNeo accepts cookie and consent banners like a visitor, then removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report 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. ScreenshotNeo is a screenshot API, not a BackstopJS reference-image comparison workflow.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does Basic authentication need to be repeated for every scenario?

The documented hook runs before each scenario. Configure it at the root for shared credentials, or use a scenario override when setup differs.

Can I use these credentials for a form-based login?

No. HTTP Basic authentication and an HTML login form are different mechanisms. Use a browser login interaction or session state for a form-based app.

Does BackstopJS compare the screenshots automatically?

BackstopJS’s workflow compares test captures against reference images. Review the visual report and approve reference changes deliberately.

Can I use ScreenshotNeo instead of BackstopJS for visual regression?

ScreenshotNeo captures images and PDFs through an API. This guide’s BackstopJS setup is for maintaining and comparing visual reference images.