ScreenshotNeo

BlogHow-to

How to Fix Blank Screenshots of Indian Websites on a Low-RAM VPS

A blank screenshot does not prove your VPS needs more RAM. Diagnose page rendering, capture timing, Chromium versions, launch flags, and memory limits in order.

By the ScreenshotNeo team4 October 20268 min read

A blank screenshot of an Indian website does not, by itself, prove that your VPS has too little RAM. First determine whether the page loaded and rendered, whether the capture happened at the right time and viewport, whether your Chromium build and flags match your automation stack, and whether the host or container recorded memory pressure or an out-of-memory (OOM) event.

This guide uses a reproducible Chromium command, then checks the rendered DOM, live headless page, browser version and launch arguments, and Linux memory evidence. The same sequence applies whether the failing URL belongs to an Indian news site, storefront, government service, or another domain. The available evidence does not identify one cause that applies to every Indian website.

1. Reproduce the failure and save evidence

Use the same URL, Chromium binary, automation library, launch arguments, viewport, and capture timing that produced the blank image. Record the command’s exit status and the browser and automation versions. Keep the PNG and, if possible, the page’s rendered DOM and browser logs.

A screenshot file can be created even when the page has not rendered useful content. Chrome’s --dump-dom serializes the DOM after scripts have run; it is different from fetching the original HTML over HTTP. Compare the rendered DOM with the image before deciding the failure is memory-related.

URL='https://example.in/'
CHROME_BIN='chromium'  # Use the executable installed on your VPS.

"$CHROME_BIN" --version
"$CHROME_BIN" --headless --no-first-run --no-default-browser-check \
  --window-size=1365,900 --timeout=10000 \
  --dump-dom "$URL" > rendered.html 2> chromium.log
status=$?
printf 'Chromium exit status: %s\n' "$status"

"$CHROME_BIN" --headless --no-first-run --no-default-browser-check \
  --window-size=1365,900 --timeout=10000 \
  --screenshot=shot.png "$URL" 2> screenshot.log
status=$?
printf 'Screenshot exit status: %s\n' "$status"
file shot.png

Replace https://example.in/ with the exact failing URL. These commands use Chrome’s documented screenshot, viewport, and timeout options. The timeout delays capture; it does not guarantee that a particular site’s application is ready. Use a page-specific readiness signal in your automation when the site needs one.

2. Check whether the page rendered before capture

Inspect rendered.html and the browser’s standard error output. If the DOM is empty, an error page, or missing the expected content, investigate the navigation and page execution path first. Check the response, redirects, DNS and TLS connectivity, browser console errors, failed network requests, and page scripts.

If the DOM contains the expected content but the PNG is blank, focus on capture timing, viewport, and the rendering path. A page may render content after a fixed delay, after an API response, or only after an element becomes visible. Prefer waiting for a meaningful selector or application state over repeatedly increasing a generic delay.

For an automation workflow, log navigation outcomes and the readiness condition alongside the screenshot. For example, with Puppeteer, wait for a selector that exists on the target page:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 1365, height: 900 });
    page.on('console', message => console.error('page console:', message.text()));
    page.on('requestfailed', request =>
      console.error('request failed:', request.url(), request.failure()?.errorText));

    const response = await page.goto('https://example.in/', {
      waitUntil: 'domcontentloaded',
      timeout: 45000
    });
    console.log('HTTP status:', response?.status());
    await page.waitForSelector('main', { timeout: 15000 });
    await page.screenshot({ path: 'shot.png', fullPage: true });
  } finally {
    await browser.close();
  }
})().catch(error => {
  console.error(error);
  process.exitCode = 1;
});

Replace main with a selector that indicates the page is ready. Some sites do not use that element, and a selector appearing does not prove every image or delayed widget has finished loading. Choose a readiness condition that matches the content you need to capture.

3. Inspect the live headless page

When a saved DOM and screenshot do not explain the difference, inspect the running headless target with DevTools. Chrome documents using --remote-debugging-port=0 to expose a headless page for inspection. Keep the debugging endpoint restricted to trusted local access; do not expose it publicly.

chromium --headless --remote-debugging-port=0 \
  --user-data-dir=/tmp/chromium-debug-profile \
  --window-size=1365,900 https://example.in/

Read Chromium’s output for the debugging address, then connect DevTools from a trusted environment using your established secure access method. Inspect the page itself: it may show a real site error, a browser error page, incomplete loading, or correct content that your capture path is missing. The presence of a PNG alone is not evidence that rendering succeeded.

4. Verify Chromium, automation, and launch arguments

Record the exact versions of Chrome or Chromium, the driver if one is used, and the automation library. Headless behavior has changed across releases: Chromium’s documentation notes that chrome-headless-shell was available from M118, and that the older Headless implementation was removed from the Chrome binary in M132. Do not assume an old headless command behaves the same on a newer installation.

Confirm which executable your automation actually launches and inspect its effective arguments. Chromium’s switch documentation warns that switches can be temporary, change, or be removed; chrome://version shows the command line for a running Chrome instance. Compare that command line with the one you intended to use before changing flags.

Avoid copying a list of flags from an unrelated deployment as a permanent fix. In particular, do not prescribe --no-sandbox as a general performance or blank-image fix. Removing the sandbox changes the browser’s security posture and does not necessarily address an empty page, early capture, or OOM. Likewise, the evidence here does not establish --disable-gpu or another extra switch as universally required for modern headless captures.

5. Check host and cgroup memory during capture

Chromium uses multiple processes. Browser and renderer memory use depends on the workload, so one process’s resident set size (RSS) is not enough to diagnose OOM. Collect system, process, and container evidence while reproducing the problem:

free -h
vmstat 1 10
ps -eo pid,ppid,rss,stat,comm,args --sort=-rss | head -n 20
journalctl -k --since '15 minutes ago' | grep -Ei 'oom|out of memory|killed process' || true

# On cgroup v2 systems, inspect the current cgroup where available:
cat /sys/fs/cgroup/memory.current 2>/dev/null || true
cat /sys/fs/cgroup/memory.max 2>/dev/null || true
cat /sys/fs/cgroup/memory.events 2>/dev/null || true

On systems using cgroup v1, the counter paths differ; check the host’s mounted memory-controller files instead of treating a missing cgroup v2 file as proof that there is no limit. A container’s memory limit can be lower than the VPS’s total RAM. Linux’s memory-controller documentation describes a low memory limit and little or disabled swap, together with anonymous memory pressure, as possible contributors to cgroup OOM. Those are possible causes, not a diagnosis for every blank screenshot.

If logs or counters show a killed renderer or OOM event, reduce simultaneous browser jobs and pages, stop unnecessary workers, and check whether the configured cgroup limit and swap settings fit the workload. If no memory evidence appears, do not upgrade RAM solely because the image is blank. There is no universal RAM or swap number established here.

6. Match the evidence to the fix

Evidence Likely area to investigate Next action
Empty or error-page DOM, with navigation or network errors URL response, redirects, DNS, TLS, connectivity, or page scripts Resolve the observed request or script failure before changing VPS size.
Expected content in the DOM, blank or incomplete screenshot Capture timing, readiness condition, viewport, or rendering path Wait for a page-specific signal, confirm viewport, and inspect the live target.
Renderer crash, kernel OOM message, or cgroup memory event Concurrency, cgroup limit, swap, or available host capacity Reduce concurrent work and correct the evidenced resource constraint.
Behavior changes with browser or automation version Headless implementation or launch-argument mismatch Record versions, inspect actual arguments, and use a supported headless mode.
Only one Indian domain fails That URL’s response, scripts, requests, or site-specific behavior Collect domain-specific logs; the domain’s location alone does not identify the cause.

7. Reduce resource pressure and improve reliability

  • Limit concurrent browser processes and pages, especially when multiple captures run in the same cgroup.
  • Close each page and browser in a finally block so failed navigations do not leave workers consuming memory.
  • Set explicit navigation timeouts and page-specific readiness checks. Record timeouts as failures; do not save them as successful screenshots.
  • Use a fixed viewport when comparing runs so a viewport difference is not mistaken for a rendering failure.
  • Keep browser, automation, viewport, URL, exit status, and relevant logs with each reproduction so version changes are distinguishable from site changes.
  • When considering a VPS change, compare the actual cgroup/container limit, concurrent job demand, swap availability, CPU, disk I/O, and price. No provider, plan, or minimum machine size is established by the available evidence.

8. Troubleshooting common errors

Symptom Possible cause What to do
PNG exists but is all white Capture occurred before meaningful rendering, or the page itself rendered blank Compare the rendered DOM, console and network logs; wait for a page-specific readiness signal.
Screenshot is clipped or has unexpected dimensions Viewport or full-page capture settings differ from expectations Set the viewport explicitly and verify the capture mode and output dimensions.
Navigation timeout Slow response, long-running requests, or unreachable URL Inspect request failures and response timing; wait for the state you need rather than assuming every request will become idle.
Browser or renderer exits during capture Crash, incompatible build/driver, or host/cgroup resource pressure Check browser and automation versions, exit logs, kernel OOM messages, and cgroup counters.
Old headless flag has no effect or behavior changed after upgrade Headless implementation or switch changed across Chromium releases Verify the installed version and actual launch command; consult the current Chromium documentation for that build.
It works locally but not on the VPS Different binary, arguments, network path, environment, or memory limit Compare versions, launch arguments, URL response, cgroup limit, and logs between both environments.
Only a particular Indian site is blank Site-specific response, script, request, or readiness behavior Inspect that site’s DOM, console, and failed requests. Do not infer a site block or RAM shortage without evidence.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. Its one-call API handles the browser setup:

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

See the ScreenshotNeo API documentation for request options. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. 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 to get 1,000 screenshots a month with no card.

FAQ

Does a blank screenshot mean the Indian website blocked Chromium?

No. A blank image alone does not establish blocking. Check the URL response, rendered DOM, console, and failed network requests to find evidence for that explanation.

Should I add more RAM first?

Only treat memory as the cause when process, kernel, or cgroup evidence supports it. First check the renderer and cgroup OOM evidence during a reproduction.

Can I fix every blank screenshot by increasing the timeout?

No. A longer delay can help when capture is early, but it cannot fix failed navigation, a browser-version mismatch, or a renderer killed by OOM. Use a readiness condition tied to the content you need.

Is --no-sandbox a safe default for VPS screenshots?

It is not a general blank-screenshot fix. Disabling the sandbox changes security behavior; investigate the actual launch error and environment before considering sandbox configuration.