ScreenshotNeo

BlogHow-to

Chrome Headless Screenshot Missing Webpage Content on Linux: Fixes

Diagnose blank or incomplete Chrome Headless screenshots on Linux by checking page readiness, launch errors, viewport, fonts, and GPU rendering.

By the ScreenshotNeo team4 October 20268 min read

A Chrome Headless screenshot on Linux can miss webpage content for several different reasons: the page may not be ready when capture starts, navigation may have failed or redirected, the browser may have runtime problems, the viewport may differ from the intended layout, or fonts and GPU-dependent rendering may be unavailable. Start by checking what the page contained at the moment of capture; there is no universal flag or delay that fixes every blank screenshot.

Record the exact Chrome or Chromium version, Puppeteer version, Linux distribution and container base, launch arguments, target URL, viewport, and screenshot method. Then use the checks below to match the observed symptom to the next useful step.

1. Check what the page looked like at capture time

A screenshot records the rendered page at a particular moment. First establish whether Chrome navigated to the intended page and whether the expected content existed in the DOM before the screenshot call.

  1. Record the final URL after redirects and the document title.
  2. Check the DOM for a known selector or expected text.
  3. Collect page console errors and failed network requests.
  4. Compare those observations with the screenshot and note exactly what is absent.

A blank image with an unexpected final URL points toward navigation, redirects, blocked requests, or a page-level error. If the DOM has the content but the image does not, investigate capture timing, viewport, or rendering instead.

2. Wait for page-specific readiness in Puppeteer

For JavaScript-rendered pages, wait for a selector that appears only when the content you need is present. A generic sleep can hide a race on one run and fail on another; use a delay only when the page has a known timing requirement and keep it bounded.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });

    page.on('console', message => console.error('PAGE CONSOLE:', message.type(), message.text()));
    page.on('requestfailed', request => console.error('REQUEST FAILED:', request.url(), request.failure()?.errorText));

    const response = await page.goto('https://example.com', {
      waitUntil: 'domcontentloaded',
      timeout: 30000
    });
    console.log('HTTP status:', response?.status());
    console.log('Final URL:', page.url());
    console.log('Title:', await page.title());

    // Replace this with a selector that proves your target content is ready.
    await page.waitForSelector('[data-page-ready="true"]', { timeout: 15000 });
    const text = await page.$eval('main', element => element.innerText);
    console.log('Main text:', text.slice(0, 500));
    await page.screenshot({ path: 'page.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Replace the sample URL and selector with the target page and a meaningful readiness signal. If the application exposes a documented ready state, wait for that condition. Puppeteer’s screenshot guide demonstrates waiting for a selector before capture. Puppeteer: Screenshots.

3. Use Chrome’s command-line capture with an explicit viewport

When reproducing outside Puppeteer, specify the viewport and use a bounded capture timeout for pages that need time to render. Confirm the output path and inspect the resulting image dimensions.

google-chrome --headless --no-sandbox \
  --window-size=1440,900 \
  --timeout=15000 \
  --screenshot=page.png \
  https://example.com

The exact binary name and supported flags depend on how Chrome or Chromium was installed. Check the installed binary’s help and version before relying on a flag. Chrome documents command-line screenshots, viewport sizing, and a capture timeout in its headless documentation: Chrome Headless.

The example includes --no-sandbox only to make the command’s sandbox choice visible: do not use it as a routine fix. Puppeteer strongly discourages disabling the sandbox and says to consider it only when the content is absolutely trusted. Prefer a correctly configured sandbox.

4. Diagnose by symptom

Observed symptom Evidence to collect Next check
Blank page or unexpected page Final URL, title, navigation result, DOM, failed requests Check redirects, navigation errors, blocked resources, and page errors.
Content appears after capture DOM and selector state at capture time Wait for a page-specific selector or application-ready condition.
Text or glyphs are absent or wrong Whether DOM text exists; affected scripts and installed fonts Check Linux font availability, especially for CJK characters.
Layout is clipped or different Configured viewport and image dimensions Set explicit viewport dimensions and check screenshot mode.
GPU- or WebGL-dependent area is missing Browser mode, GPU logs, feature requirements Check the browser mode and GPU setup for the specific rendering need.
Browser exits or will not launch stderr, missing shared libraries, sandbox errors, distribution Resolve runtime dependencies and sandbox or AppArmor configuration.

5. Fix Linux startup and runtime problems

A browser that never starts is a different problem from a browser that starts and captures an empty page. If launch fails or Chrome exits early:

  • Read the browser’s stderr and the automation library’s launch error.
  • Verify required shared libraries for the installed Chrome or Chromium package.
  • Review sandbox setup instead of immediately adding a sandbox-disabling flag.
  • On Ubuntu 23.10 and later, check Puppeteer’s documented AppArmor consideration for Chrome for Testing binaries.

Use the troubleshooting guidance for the exact browser package and environment. The Puppeteer guide covers Linux dependencies, sandbox errors, AppArmor considerations, fonts, and headless-shell behavior: Puppeteer: Troubleshooting. Container images and distributions differ, so copy the error output and runtime details into your diagnosis rather than installing packages at random.

6. Check viewport, screenshot mode, and output

A page can render correctly but produce an image that appears blank or incomplete if the captured region does not include the content, if the layout responds differently at the chosen width, or if the output being inspected is not the file just created.

  • Set an explicit viewport in Puppeteer or use Chrome’s documented --window-size option.
  • Check whether the capture is viewport-only or full-page.
  • Inspect the output file’s dimensions and verify its destination and modification time.
  • For a diagnosis, compare a headed run with the same URL and viewport; this can reveal mode-specific behavior, but it does not mean headless operation requires a display server.

Chrome Headless can run without Xvfb or another visible display server. Adding Xvfb solely because no desktop is present is not a general fix. See Chrome’s Headless documentation.

7. Investigate fonts and GPU only when symptoms point there

Missing glyphs or altered typography

If the expected text is present in the DOM but characters are missing, boxes appear empty, or typography differs, verify that the system has fonts covering the page’s scripts. Puppeteer notes that CJK rendering may require additional fonts. Font problems generally explain text appearance or glyph gaps; they do not explain every fully blank screenshot.

GPU-dependent content

Check GPU only when the missing region depends on GPU features such as WebGL or a particular compositing path. Puppeteer notes that chrome-headless-shell disables GPU compositing. Chromium’s guidance says --enable-gpu disables forced software rendering in headless Chrome; whether it helps depends on the browser mode and the actual feature involved. Do not treat it as a universal blank-page flag.

Sources: Puppeteer troubleshooting and Chromium: Using GPU hardware in headless Chrome.

8. A practical diagnostic checklist

  • [ ] Exact Chrome or Chromium binary and version recorded.
  • [ ] Puppeteer and Node.js versions recorded.
  • [ ] Linux distribution, container base, and relevant runtime dependencies recorded.
  • [ ] Launch arguments, screenshot method, viewport, and target URL recorded.
  • [ ] Final URL, title, navigation status, console errors, and failed requests checked.
  • [ ] Expected selector or content checked immediately before capture.
  • [ ] Screenshot mode, output path, and image dimensions verified.
  • [ ] Fonts or GPU investigated only when the missing content’s appearance points to them.

When asking for help, include these details and describe exactly what is missing. “Blank” can mean an all-white image, a page background without text, content cut off below the fold, or one missing widget; those symptoms lead to different checks.

9. Performance, reliability, and cost considerations

Wait for the narrowest readiness condition that proves the required content is available. Waiting for every network connection to become idle can stall on pages that keep analytics, polling, or streaming connections open; a page-specific selector is often a more useful completion signal. Keep navigation and selector timeouts bounded so a slow or broken page does not hold a job indefinitely.

For reliable captures, log browser and library versions, final URL, status, readiness outcome, and failure details alongside the image. Pinning the browser and runtime in a container can make changes easier to diagnose, though it does not make target pages deterministic. Sites can change content, block automation, or depend on external resources.

Local browser captures avoid a per-shot screenshot API charge, but they require you to maintain Chrome dependencies, fonts, sandbox configuration, and capture infrastructure. If you use a hosted screenshot service, compare its billing rules for failed loads, bot checks, blank pages, and cache hits as well as its feature and request costs.

10. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request can return a PNG, JPEG, WebP, or PDF. Its clean capture flow accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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 ScreenshotNeo API documentation for request options and response details. 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. All features are on every plan.

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

Frequently asked questions

Why is my Puppeteer screenshot blank?

There is no single cause. Check the final URL and DOM first, then page readiness, runtime errors, viewport, and only symptom-relevant font or GPU issues.

How do I make Headless Chrome wait for JavaScript content?

Wait for a selector or application-ready condition that indicates the needed content has rendered. A fixed delay is not a universal readiness test.

Does headless Chrome need Xvfb on Linux?

No. Headless Chrome does not require a visible display server simply because the machine has no desktop.

Should I always add --enable-gpu?

No. Investigate it when the missing area depends on GPU behavior, and account for differences between full Chrome and chrome-headless-shell.

Is there one flag that fixes missing content?

No documented universal fix exists. Choose the next check based on whether the browser failed to launch, the page was not ready, the viewport was wrong, or a specific rendering dependency was missing.