ScreenshotNeo

BlogHow-to

Why Does Chrome Headless Screenshot Only the Visible Viewport?

Chrome Headless captures the requested screenshot dimensions, not automatically the whole document. Use Puppeteer’s fullPage option for a full-page image.

By the ScreenshotNeo team4 October 20266 min read

Chrome Headless’s command-line --screenshot captures the target page at the requested screenshot dimensions. It does not automatically capture the entire document. --window-size=WIDTH,HEIGHT changes those dimensions; it is not a full-page switch. For a full-page image, use Puppeteer’s fullPage: true option or configure a DevTools Protocol capture beyond the viewport.

This distinction is useful when a screenshot looks correct across the visible browser area but stops before the bottom of a long page. Capture size, full-document bounds, and page readiness are separate concerns.

Why the command-line screenshot stops at the viewport

Chrome’s documented CLI supports --screenshot and --window-size. The documented screenshot command saves an image of the target page, while the window-size flag specifies its dimensions. Chromium’s command handler passes the parsed width and height into screenshot parameters. Neither behavior means “measure the whole document and capture it.”

For example, increasing the height can produce a taller fixed-size screenshot, but it may still omit content beyond the requested capture area. The CLI reference does not document a --full-page flag.

Choose the right capture method

Method Use it for Tradeoff
Chrome CLI --screenshot plus --window-size A simple screenshot at specified dimensions Does not request full-document capture.
Puppeteer fullPage: true A full-page image through browser automation Dynamic and lazy content may need additional page-specific preparation.
DevTools Protocol Page.captureScreenshot Low-level control over screenshot bounds Set suitable bounds and check compatibility with the Chrome version in use.
Chrome CLI --print-to-pdf A printable document Produces a PDF, not a screenshot image.

Use Chrome CLI for a fixed screenshot size

Use the CLI when you want a straightforward screenshot with known dimensions. Replace the example URL and dimensions as needed:

chrome --headless --window-size=1440,1200 --screenshot=page.png https://example.com

The screenshot is requested at 1440 by 1200 pixels. It is not guaranteed to include a document taller than that. The exact executable name can vary by installation. Chrome’s CLI also provides --timeout and --virtual-time-budget to control capture timing; those controls do not themselves make the image full-page or guarantee that application-specific asynchronous work has finished.

Capture the full page with Puppeteer

Puppeteer’s fullPage screenshot option requests an image of the full page; the option defaults to false. This runnable Node.js example navigates to a page, waits for network activity to settle, and saves a PNG:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

Install Puppeteer in a Node.js project with npm install puppeteer. The code uses a common readiness condition, not a guarantee that every site has completed all work. A page that loads content after a timer, user action, or scroll may need a selector wait, an explicit delay, or page-specific scrolling before capture.

Wait for a page-specific condition when needed

If the page has a known element that appears only after its data is ready, wait for it before taking the screenshot:

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-report-ready="true"]', { timeout: 15000 });
await page.screenshot({ path: 'report.png', fullPage: true });

Use a selector that actually represents readiness on the target site. If content is lazy-loaded only when scrolled into view, scroll through the page before capturing and allow the newly requested content to render. A full-page capture expands the capture area; it does not promise to trigger every site’s lazy-loading behavior.

Use DevTools Protocol for low-level capture

The DevTools Protocol method Page.captureScreenshot has a captureBeyondViewport option, documented with a default of false. A protocol client must enable the needed behavior and provide the intended capture region through the method’s clip and scale parameters. This gives finer control than Puppeteer’s high-level option, but the protocol reference is tip-of-tree and can change; confirm the method and parameters supported by the Chrome version you run.

For most scripts, Puppeteer’s fullPage: true is the simpler interface. Use the protocol directly when you need explicit bounds or are integrating with a DevTools Protocol client already in your system.

Diagnose missing or clipped content

Symptom Likely cause What to try
Image ends at the requested height CLI screenshot dimensions were mistaken for full-page capture. Use Puppeteer fullPage: true, or set appropriate bounds and beyond-viewport capture through the protocol.
Bottom section is blank or incomplete Content was not ready when capture happened. Wait for a page-specific selector or condition; use a timeout only when the page has no better readiness signal.
Images or cards are missing farther down They load lazily or on scroll. Scroll through the page and wait for the content to appear before capturing.
--window-size seems ignored The command may be using a different Chrome executable, wrapper, or argument format. Check the exact binary and invocation, then confirm the dimensions in the resulting image.
An old Headless tutorial no longer works Headless packaging changed. Chromium says old Headless functionality was removed from the Chrome binary in M132. Use current Chrome Headless, or migrate old Headless workflows to the separately distributed chrome-headless-shell.
Protocol capture behavior differs across machines Protocol support and defaults can vary by Chrome version. Check the protocol implementation for the exact Chrome build and explicitly set bounds and capture options.

Timing, reliability, and resource considerations

  • Wait for the right state. Network-idle conditions help with many pages but do not establish that every app has finished rendering. Prefer a selector or application-specific readiness signal when available.
  • Expect full-page captures to cost more resources. A tall page produces a larger image and may require more browser memory and encoding time than a viewport screenshot. Very long pages can be slow or exceed practical memory limits; capture a specific element or split the work when a single image is not essential.
  • Prepare lazy content deliberately. Full-page bounds and page loading are independent. Scroll-triggered content may not exist until the page is scrolled or an interaction occurs.
  • Keep browser versions and wrappers visible in debugging. Record the Chrome version, the executable path, the exact arguments, and whether a framework changes viewport or screenshot behavior.
  • Choose an output format for the task. Use an image screenshot for pixels and --print-to-pdf when the desired artifact is a printable document.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Its full-page capture option loads lazy images; other controls include viewport presets, custom viewport dimensions, selector capture, and wait conditions. See the ScreenshotNeo API documentation.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.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(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed, and response headers identify the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account for 1,000 screenshots a month with no card.

FAQ

Does Chrome Headless have a documented --full-page flag?

The Chrome CLI reference documents --screenshot and --window-size, but not a --full-page switch. Use browser automation or DevTools Protocol for full-page image capture.

Does fullPage: true make every lazy-loaded item appear?

No. It requests full-page screenshot bounds. Content that only loads after scrolling or an application-specific action may need to be populated first.

Is a PDF the same as a full-page screenshot?

No. --print-to-pdf creates a printable document. Use an image capture API when you need a PNG, JPEG, or WebP screenshot.

Sources