Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
browser automation

How to Include the URL in a Playwright Screenshot

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

Playwright does not add the browser’s address bar to an image produced by page.screenshot(). To make the URL visible, read the final address with page.url(), render that value as an overlay inside the page, and then capture the screenshot. If you only need to identify the file, store the URL in a filename or metadata record instead. Playwright’s documented screenshot options cover viewport, full-page, element and buffer captures, not browser-window chrome (screenshots guide; Page API).

What Playwright can—and cannot—capture

page.screenshot() captures rendered web content in the page. It does not capture the operating-system window, browser tabs or address bar. A full-page screenshot means the full scrollable page is rendered as though it were very tall; it changes the captured area but does not add browser interface elements (official screenshots guide).

Therefore, “include the URL” has two different solutions:

  • Visible URL: inject a label into the document before the screenshot.
  • Non-visible identification: write page.url() to a log, metadata store or filename beside the image.

Use page.url() after navigation and redirects. It represents the address currently loaded, which may differ from the URL you originally requested.

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

Inject a URL overlay before taking the screenshot

The following Node.js example launches Chromium, navigates to a page, adds a fixed URL bar, captures a PNG and removes the bar. The marker makes repeated runs safe by replacing an existing overlay rather than creating duplicates.

import { chromium } from 'playwright';

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

await page.goto('https://example.com', { waitUntil: 'networkidle' });

const url = page.url();
await page.evaluate((url) => {
  document.getElementById('__playwright_url_overlay')?.remove();

  const label = document.createElement('div');
  label.id = '__playwright_url_overlay';
  label.textContent = url;
  Object.assign(label.style, {
    position: 'fixed',
    top: '0',
    left: '0',
    right: '0',
    zIndex: '2147483647',
    boxSizing: 'border-box',
    padding: '8px 12px',
    background: '#fff',
    color: '#111',
    font: '14px sans-serif',
    lineHeight: '20px',
    overflowWrap: 'anywhere',
    boxShadow: '0 1px 4px #0004'
  });
  document.body.appendChild(label);
}, url);

await page.screenshot({ path: 'screenshot.png', fullPage: true });

// Keep later captures unmodified.
await page.evaluate(() => {
  document.getElementById('__playwright_url_overlay')?.remove();
});

await browser.close();

Install Playwright with npm install playwright and install the browser binaries with npx playwright install chromium when your project does not already provide them. Save the script as an ES module (for example, shot.mjs) and run node shot.mjs.

Overlay versus a content banner

position: fixed keeps the label at the top of the viewport. On a full-page capture, Playwright stitches the page; the fixed element can appear at the top of the resulting image, but exact behavior can vary with page layout and the installed Playwright version. If you want the URL to consume layout space instead, use position: static (or insert the label as the first child of body) so the page moves down. That is less likely to cover content, but it changes the page’s visual layout.

For a viewport-only image, omit fullPage: true. For one component, locate it and call locator.screenshot(); the overlay must be inside the element being captured if the URL should appear in that element image.

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

Control long and sensitive URLs

Long query strings wrap because of overflowWrap: anywhere. You can instead display a shortened label while preserving the complete URL in a sidecar log. Do not expose tokens, session identifiers or personal data in an image: URLs can contain credentials or sensitive query parameters. A safer pattern is to redact before setting textContent and retain the unredacted value only in an access-controlled log.

Make the overlay reusable

Put the injection in a helper so every capture uses identical styling and cleanup:

async function addUrlLabel(page, {
  selector = '#__playwright_url_overlay',
  background = '#fff',
  color = '#111'
} = {}) {
  const url = page.url();
  await page.evaluate(({ url, selector, background, color }) => {
    document.querySelector(selector)?.remove();
    const label = document.createElement('div');
    label.id = selector.startsWith('#') ? selector.slice(1) : '__playwright_url_overlay';
    label.textContent = url;
    Object.assign(label.style, {
      position: 'fixed', top: '0', left: '0', right: '0',
      zIndex: '2147483647', boxSizing: 'border-box', padding: '8px 12px',
      background, color, font: '14px sans-serif', overflowWrap: 'anywhere'
    });
    document.body.appendChild(label);
  }, { url, selector, background, color });
}

await addUrlLabel(page);
await page.screenshot({ path: 'labeled.png' });
await page.evaluate(() => document.getElementById('__playwright_url_overlay')?.remove());

Using textContent rather than innerHTML treats the URL as text, so characters in a query string cannot become markup. The very high z-index helps the label sit above ordinary site elements, although a page can still create unusual stacking contexts.

Capture the URL without changing the image

If the requirement is traceability rather than a visible label, keep the screenshot clean and write a sidecar record:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { writeFile } from 'node:fs/promises';

const requested = 'https://example.com';
await page.goto(requested, { waitUntil: 'domcontentloaded' });
const finalUrl = page.url();
await page.screenshot({ path: 'example.png', type: 'png' });
await writeFile('example.json', JSON.stringify({ requested, finalUrl }, null, 2));

This preserves the exact final URL after redirects while leaving page pixels untouched. You can also include a sanitized URL in a filename, but filesystem limits and special characters make a JSON or database record more reliable.

Choose the right capture mode

Need Playwright approach URL-label consideration
Visible browser-sized image page.screenshot({ path }) Fixed overlay appears in the viewport.
Entire scrollable page page.screenshot({ fullPage: true }) Plan whether the label overlays the top or pushes content down.
One component locator.screenshot({ path }) Inject the label inside the captured element, or use sidecar metadata.
In-memory processing page.screenshot({ type: 'png' }) returns a buffer Store page.url() with the buffer in your own record.
Printable document page.pdf() Use the documented PDF header/footer templates instead of an image overlay.

Use a PDF header when an image is not required

Playwright’s Page API documents displayHeaderFooter for PDFs. PDF header and footer templates provide a url class that prints the document location. This is a separate output path from screenshots: template scripts are not evaluated and page styles are not visible inside the templates (Page API reference).

const finalUrl = page.url();
await page.pdf({
  path: 'page.pdf',
  format: 'A4',
  displayHeaderFooter: true,
  headerTemplate: '<div style="font-size:9px;width:100%;padding:0 12px"><span class="url"></span></div>',
  footerTemplate: '<div style="font-size:9px;width:100%;padding:0 12px;text-align:right">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>',
  margin: { top: '32px', bottom: '32px' }
});

Use this when a URL on every printed page is preferable to modifying screenshot pixels. If you need both a PDF and an image, perform the PDF operation separately and keep the overlay helper for the image.

Timing, redirects and dynamic pages

Read the URL at the correct point

Call page.url() after the navigation you want to document. If a login flow, canonical redirect or client-side route change occurs afterward, wait for that transition and read the URL again immediately before injection. A URL captured too early can identify a different state than the pixels.

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

Wait for the content that matters

waitUntil: 'networkidle' can be useful for relatively quiet pages, but applications with analytics, polling or WebSockets may never become idle. Prefer a specific readiness condition when possible:

await page.goto('https://example.com/dashboard', { waitUntil: 'domcontentloaded' });
await page.locator('[data-testid="dashboard-ready"]').waitFor({ state: 'visible' });
const url = page.url();

After injecting the overlay, take the screenshot in the same task. If a framework re-renders body, it may remove your label; inject it after the final render or use a page-level hook that runs at the required point.

Lazy-loaded content and full-page captures

Full-page capture can expose content that was not visible in the initial viewport. Scroll or wait for the site’s own lazy-load trigger before adding the URL label. Otherwise the screenshot can contain incomplete images even though the URL is correct.

Common failures and fixes

  • The address bar is missing. This is expected: page.screenshot() is a page capture, not a browser-window capture. Inject a label or use a separate desktop/window capture tool.
  • The label is not visible. Confirm that the injection runs after page.goto(), that document.body exists, and that you are capturing the same page or locator into which you inserted it. Check the generated image format and path.
  • The label is behind a modal or header. Use a high z-index, fixed positioning and a solid background. Site-specific stacking contexts can still require inserting the label nearer the top-level document.
  • The URL appears twice. Remove an earlier element by a stable ID before appending. The helper above is idempotent.
  • The URL is stale. Read page.url() immediately before injection, after redirects and route changes have settled.
  • Full-page output covers content. Change the label to normal document flow, add top padding to the page, or place the URL in a sidecar record instead of overlaying it.
  • Characters look wrong. Set an explicit font and ensure the page is captured after fonts load. Keep the URL in textContent; do not build HTML from it.
  • Navigation times out. Increase the navigation timeout only when justified, wait for a specific selector instead of network idle, and record the failed URL and error for retry handling.
  • Secrets leak into screenshots. Redact credentials and sensitive query parameters before display, and avoid storing unprotected images.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability and repeatable output

Launching a browser for every URL is expensive. Reuse one browser process and create isolated contexts or pages for batches. Set a fixed viewport, color scheme, locale, timezone and device scale factor when visual consistency matters. Use PNG for lossless text and UI, JPEG or WebP when smaller files are more important.

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.

Keep navigation, readiness, overlay injection and capture in one recorded job. Save the requested URL, final page.url(), timestamp, viewport and capture options alongside the artifact. For retries, create a fresh page or context after a failed navigation; do not assume a partially loaded document is valid. Treat bot checks, authentication walls and blank responses as distinct outcomes rather than silently storing them as successful screenshots.

When many pages share a template, centralize the overlay CSS and use a versioned helper. That prevents a style change from making historical images difficult to compare. Remove the overlay after each capture if the same page will produce an unlabeled image later.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server if you would rather make one request than maintain Playwright browser code. It can accept consent banners before capture and remove more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and whether the request was billed.

Its MCP server exposes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients. Every plan includes features such as full-page capture, CSS-selector element shots, custom CSS and JavaScript, waits, request blocking, cookies and headers, device presets, PDFs and signed links.

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

For a direct image request, see the ScreenshotNeo API documentation:

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

The Free plan includes 1,000 screenshots each month without a card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free. Create a free ScreenshotNeo account to try it.

Practical decision checklist

  • Need the URL printed in pixels? Inject a fixed or flow-based label immediately before page.screenshot().
  • Need an untouched image with auditability? Store page.url() beside the file.
  • Need a URL on every printed page? Generate a PDF with displayHeaderFooter and the documented url template class.
  • Need stable comparisons? Fix viewport, device scale, locale, timezone, readiness conditions and overlay styling.
  • Need high-volume or low-maintenance capture? Use an API such as ScreenshotNeo and inspect its verdict and billing headers.

Frequently Asked Questions

Does page.screenshot() capture the browser address bar?

No. It captures rendered page content. Add the URL as page content or record it separately.

Which URL should I display after a redirect?

Read page.url() after the redirect and after any client-side route change you want the screenshot to represent.

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

Can I add a URL to an element screenshot?

Yes, but the label must be inside the element being captured. Otherwise keep the element image clean and store the URL as metadata.

Is a PDF URL header the same as a screenshot overlay?

No. PDF headers are a separate Page API feature and can repeat on printed pages; they do not add a header to screenshot pixels.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.