HTTP Basic Authentication: Definition and Examples

HTTP basic authentication sends a username and password in a base64 Authorization header. How the 401 challenge works and how to pass it in a headless browser.

By Simon Chiu · Updated

HTTP basic authentication is the simplest login scheme built into HTTP: the client sends Authorization: Basic followed by the base64 encoding of username:password with every request, and the server answers 401 Unauthorized when the credentials are missing or wrong.

It is defined in RFC 7617. You meet it most often in front of staging sites, internal dashboards and preview deployments. This entry is part of the screenshot glossary.

What it means in plain terms

When you open a protected page in a normal browser, a small native dialog asks for a username and password. That dialog is basic auth. There is no login form, no session cookie and no logout: the browser simply remembers the pair and sends it again with each request to that site.

A headless browser has no person to fill in that dialog. You have to give it the credentials in advance, or the capture shows an error page.

The challenge and response

sequenceDiagram
  participant B as Browser
  participant S as Server
  B->>S: GET /dashboard
  S-->>B: 401 Unauthorized<br/>WWW-Authenticate: Basic realm="staging"
  B->>B: base64("demo:s3cret") = ZGVtbzpzM2NyZXQ=
  B->>S: GET /dashboard<br/>Authorization: Basic ZGVtbzpzM2NyZXQ=
  S-->>B: 200 OK

How it works technically

  1. The server returns 401 with a WWW-Authenticate: Basic realm="..." header. The realm is a label that tells the client which set of credentials applies.
  2. The client joins username and password with a colon and base64-encodes the result. You can reproduce the header value yourself:
printf 'demo:s3cret' | base64
# ZGVtbzpzM2NyZXQ=
curl -u demo:s3cret https://staging.example.com/   # curl builds the same header
  1. The client repeats the request with Authorization: Basic <value> and keeps sending it for that origin.

The scheme has hard limits: the password travels with every request, a username cannot contain a colon, and there is no standard way to log out. That is why apps use sessions or bearer tokens for real users and keep basic auth for gates in front of whole sites.

How to pass it in a headless browser

We tested both libraries against a local server that requires demo / s3cret. With correct credentials both returned 200. With a wrong password neither hung or threw: each loaded the server's 401 page and would have screenshotted it. That is why both scripts check the status before capturing.

Puppeteer

import puppeteer from 'puppeteer';

const target = process.argv[2] ?? 'https://staging.example.com/';
const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  // Answers the 401 challenge for this page; call it before goto.
  await page.authenticate({
    username: process.env.STAGING_USER,
    password: process.env.STAGING_PASS,
  });
  const response = await page.goto(target);
  if (!response.ok()) throw new Error(`HTTP ${response.status()} for ${target}`);
  await page.screenshot({ path: 'staging.png' });
  console.log('status', response.status());
} finally {
  await browser.close();
}

Playwright

import { chromium } from 'playwright';

const target = process.argv[2] ?? 'https://staging.example.com/';
const browser = await chromium.launch();
try {
  const context = await browser.newContext({
    httpCredentials: {
      username: process.env.STAGING_USER,
      password: process.env.STAGING_PASS,
      origin: new URL(target).origin, // send credentials to this origin only
    },
  });
  const page = await context.newPage();
  const response = await page.goto(target);
  if (!response.ok()) throw new Error(`HTTP ${response.status()} for ${target}`);
  await page.screenshot({ path: 'staging-pw.png' });
  console.log('status', response.status());
} finally {
  await browser.close();
}

The origin option keeps Playwright from sending the password to third-party hosts the page loads, such as a CDN or an analytics domain.

Tested on 2026-10-15 with Node 22.18.0, [email protected] (Chrome 148, run with headless: 'shell') and [email protected] (Chromium 156).

RenderScreenshot

Pass the credentials in network.auth in a POST request. The browser answers the challenge for you:

curl -X POST https://api.renderscreenshot.com/v1/screenshot \
  -H "Authorization: Bearer $RS_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"url\": \"https://staging.example.com/\",
    \"network\": {
      \"auth\": { \"type\": \"basic\", \"username\": \"$STAGING_USER\", \"password\": \"$STAGING_PASS\" }
    }
  }" \
  --output staging.png

The Node.js SDK method is .authBasic(user, pass); in Python it is .auth_basic(user, pass). Note that two different Authorization values are involved: your RenderScreenshot API key authenticates you to the API, and the basic auth pair authenticates the browser to your site. See network options and the authenticated pages guide, which also covers cookies and a check that you did not capture a login page. This request was not run for this page.

Common mistakes

  • Not checking the status code. A wrong password produces a clean screenshot of an error page.
  • Credentials in the URL (https://user:pass@host). Browsers restrict this form, and the password ends up in logs and history.
  • Sending the pair to every host. Scope credentials to the origin that needs them.
  • Basic auth over plain HTTP. The base64 string is readable by anyone on the network.
  • Putting staging credentials in a public URL, such as an og:image link. Use a signed URL for public links and keep site credentials in server-side requests.

To capture a password-protected preview without running Chrome, get a free API key.

Frequently asked questions

Is HTTP basic authentication secure?
Only over HTTPS. Base64 is an encoding, not encryption, so anyone who can read the request can read the password. Over TLS it is acceptable for simple cases such as a staging site.
Can I put the username and password in the URL?
The https://user:pass@host form is deprecated, logged by many tools and blocked by browsers in several contexts. Use the Authorization header or your tool's credentials option.

Related in this topic

Try RenderScreenshot free

150 free credits every month, no credit card required.