Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Set the headers before navigation. In Playwright or Puppeteer, call the page-level extra-header method, then open the URL and capture it. The headers are added to requests initiated by that page, not just the first document request, so use this for locale, preview, tenant, feature-flag and similar request-dependent pages.

What setting a header actually changes

An HTTP header is metadata sent with a request, such as Accept-Language, a preview token or an application-specific tenant identifier. A screenshot browser can send those values while it loads the page, allowing the server and page resources to respond accordingly.

Both browser APIs document the same important scope: extra headers are sent with every request the page initiates. That can include the main document and later resource requests. It does not guarantee that a header is attached to requests made by another page, a separate browser context, an external service, or code that bypasses the page. Header order is not a contract. HTTP field names are case-insensitive, and Puppeteer notes that its outgoing names are lowercased.

Use string values. Keep credentials in environment variables or a secret manager; never put a live token in client-side JavaScript, a public repository or a screenshot that exposes it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Playwright: set headers before opening the URL

Install Playwright and its browser once in your project:

npm install playwright
npx playwright install chromium

This complete Node.js example reads a token from the environment, applies two headers to the page, waits for the target to load and writes a full-page PNG:

const { chromium } = require('playwright');

(async () => {
  const token = process.env.PREVIEW_TOKEN;
  if (!token) throw new Error('Set PREVIEW_TOKEN first');

  const browser = await chromium.launch();
  try {
    const page = await browser.newPage({
      viewport: { width: 1440, height: 900 },
      deviceScaleFactor: 1
    });

    await page.setExtraHTTPHeaders({
      'x-preview-token': token,
      'accept-language': 'en-US'
    });

    await page.goto('https://example.com', { waitUntil: 'networkidle' });
    await page.screenshot({ path: 'screenshot.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Playwright’s Page.setExtraHTTPHeaders API requires string values and documents page-initiated request scope. Its screenshot API supports page and element captures, including full-page output. See the Playwright Page API and Playwright screenshot documentation.

When to choose a readiness condition

  • domcontentloaded: useful when the HTML is the result you need and later scripts are irrelevant.
  • load: waits for load-event resources, but not necessarily data fetched afterward.
  • networkidle: useful for many client-rendered pages, but analytics, polling and chat connections can prevent a stable idle period.

For a deterministic capture, wait for a page-specific selector after navigation instead of assuming a generic event means the UI is complete:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.goto('https://example.com/dashboard', { waitUntil: 'domcontentloaded' });
await page.locator('[data-testid="dashboard-ready"]').waitFor();
await page.screenshot({ path: 'dashboard.png', fullPage: true });

Puppeteer: the equivalent workflow

Install Puppeteer (which downloads a compatible browser in its standard installation):

npm install puppeteer
const puppeteer = require('puppeteer');

(async () => {
  const token = process.env.PREVIEW_TOKEN;
  if (!token) throw new Error('Set PREVIEW_TOKEN first');

  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.setExtraHTTPHeaders({
      'x-preview-token': token,
      'accept-language': 'en-US'
    });

    await page.goto('https://example.com', { waitUntil: 'networkidle2' });
    await page.screenshot({ path: 'screenshot.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

The method is documented in Puppeteer’s Page.setExtraHTTPHeaders API. Its screenshot guide shows navigation before capture and options for page or element screenshots. Choose a waitUntil condition that matches the target rather than copying one blindly.

Header scope, precedence and browser caveats

Initial document versus subresources

Set headers before goto when the server must see them on the initial HTML request. Because the setting covers page-initiated requests, it can also affect scripts, stylesheets, images and XHR or fetch calls initiated by that page. It is not a promise that every network operation in the browser will carry the value.

Redirects and third-party hosts

A redirect can change the destination and origin. Do not assume a secret header is safe to forward to an unrelated host. Test the redirect chain and review the target’s policy. A page may also call a third-party API from JavaScript; browser security rules, CORS and the API’s own authentication determine what happens.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Browser-controlled headers

Some headers are controlled or normalized by the browser. You should not depend on exact casing or ordering, and an application should not use header order as an authentication signal. The documented APIs are for additional headers; they are not a general bypass for browser security controls.

Authentication is not guaranteed

A custom header can identify a request, but it does not automatically defeat login requirements, bot checks, CAPTCHAs, authorization rules or rate limits. The destination must be designed to accept that header and validate it. Test with a least-privilege token and an endpoint that reports the authenticated state without exposing secrets.

Debug the request before debugging the screenshot

  1. Confirm the process has the secret. Print only whether PREVIEW_TOKEN exists, never its value.
  2. Verify the header is set before navigation. Calling the method after goto cannot change the already-sent document request; reload after changing it.
  3. Inspect the page response. Check status, final URL and visible error text. A 401 or 403 means the server rejected the request, not that the screenshot API failed.
  4. Check the header value type. Convert configuration values to strings and remove accidental undefined, line breaks or surrounding quotes.
  5. Wait for the authenticated UI. A successful HTML response may still require an XHR. Wait for a stable selector or a known API response before capturing.
  6. Capture a diagnostic image. Temporarily add a conspicuous non-secret marker in a test environment so you can distinguish the intended variant.

Common symptoms and fixes

Symptom Likely cause Fix
Server says the header is missing Header was configured after navigation, or the request came from another page/context Create/configure the page first, then reload; apply the setting to every page that needs it
401 or 403 response Expired token, wrong audience, insufficient permission or server-side policy Validate the token and endpoint independently; do not assume a screenshot header bypasses authorization
Correct HTML, wrong screenshot state Client-side data loaded after navigation Wait for a page-specific selector or response, then capture
Header appears with different casing HTTP names are case-insensitive; Puppeteer lowercases names Compare names case-insensitively and inspect values, not presentation
Navigation hangs Persistent connections, polling, ads or third-party requests prevent idle Use domcontentloaded or load, then wait for a precise readiness selector and set a timeout
Redirect leaks or loses context Origin changed during the redirect chain Inspect final URL and redirect policy; avoid sending secrets to hosts you do not control

Hosted screenshot endpoints: when browser code is unnecessary

A managed endpoint accepts the URL and capture parameters and operates the browser for you. Screenshot API documents a repeatable header parameter in Name: value form and a POST form that accepts headers as an object. Its documentation says custom headers are sent only to the target host, which is a narrower scope than page-level browser headers. The same documentation lists viewport, full-page, format, delay, cookies and timeout options; verify current limits before relying on them: Screenshot API documentation.

Use a hosted service when you prefer a simple request and do not want to patch browser binaries, manage concurrency or keep a rendering worker healthy. Run Playwright or Puppeteer when you need browser-level event handling, custom interaction, network interception or infrastructure control.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo is a managed website screenshot API and MCP server. It supports custom headers alongside cookies, user agents and Authorization, so the header-bearing request can be made without installing Chromium. A single GET request returns PNG, JPEG, WebP or PDF.

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}`);

See the complete parameter names and header options in the ScreenshotNeo documentation. ScreenshotNeo can accept cookie banners before capture and remove more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and whether it was billed. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account to try a header-aware capture.

Performance, reliability and cost decisions

  • Reuse a browser: launch once and create pages or contexts per job; launching a new browser for every image adds substantial startup work.
  • Limit concurrency: too many simultaneous pages exhaust CPU, memory, file descriptors or the destination’s rate limit. Use a queue and bounded workers.
  • Set explicit timeouts: combine navigation, selector and overall job limits so a stalled third-party request cannot consume a worker indefinitely.
  • Control output size: full-page, high device scale factors and PDF rendering use more memory than a viewport PNG. Capture an element when the page does not need to be included.
  • Cache carefully: cache keys must include the URL and every header or cookie that changes the response. Never serve a private, header-authenticated image from a public cache.
  • Retry selectively: retry transient navigation or network failures with backoff, but do not repeatedly retry 401/403 responses or an invalid token.
  • Observe without leaking: log status, final URL, duration and a redacted request identifier; do not log Authorization values or full query strings containing secrets.

Practical decision guide

Approach Best when Header scope Main trade-off
ScreenshotNeo You want a managed capture, cleanup of consent UI and an MCP workflow Custom headers and other request controls in the hosted capture Depends on the service interface and account limits
Playwright You need modern browser automation, selectors and fine-grained waits Requests initiated by the configured page You operate browsers, workers and updates
Puppeteer You already use the Chrome automation ecosystem Requests initiated by the configured page You operate browsers and must handle readiness and scaling
Screenshot API You need a documented hosted endpoint with a header parameter Only requests to the target host, according to its documentation Vendor limits and behavior can change; recheck its docs

Security checklist

  • Store tokens in environment variables or a secret manager.
  • Use short-lived, least-privilege credentials where the application supports them.
  • Redact headers in logs and error reports.
  • Do not put secrets in URLs, HTML, screenshots or public CI artifacts.
  • Review redirects and third-party requests before sending a sensitive header.
  • Rotate a credential immediately if it appears in a capture or log.

Frequently Asked Questions

Can I set a different header for each URL in one browser process?

Yes. Create or reuse a page and call its extra-header method with the values for that job before navigation. Use separate browser contexts when cookies, storage or isolation must also differ.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Will the header be visible to JavaScript running on the page?

Not necessarily. A request header is sent on the network request; it is not automatically exposed through browser JavaScript APIs. If page code must know a value, provide an explicit, non-secret application signal instead.

Why does changing a header require a reload?

The document request has already been sent. Header configuration affects subsequent page-initiated requests, so reload or navigate again after changing the setting.

Should I rely on a specific order of HTTP headers?

No. Browser and server stacks may reorder or normalize fields. Authentication and parsing should use header names and values, never their order.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.