Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Most server-side rendering failures happen before your HTML is rendered. First prove that the deployed process has a compatible Chromium executable, its shared libraries and fonts, a writable profile and temporary directory, and a supported sandbox. Then separate navigation from application readiness, configure PDF media and color behavior explicitly, and account for your hosting platform’s lifecycle. The sequence below uses Puppeteer examples, but the same diagnostic boundaries apply to other browser automation libraries.
Start by identifying the failing stage
“Puppeteer works locally” does not prove that production can launch a browser. A failure belongs to one of four stages:
- Installation: the Node package or its browser-download script is missing from the production install.
- Launch: Chromium cannot start because the executable, shared libraries, sandbox, user permissions or writable paths are wrong.
- Page readiness: the browser starts, but the page is still loading data, fonts or client-rendered content when you capture it.
- Output: the screenshot or PDF is produced, but print media, colors, fonts, paper size or background settings make it look wrong.
Log each boundary separately. Record the Puppeteer version, expected browser revision, executable path, operating-system image, runtime user, final URL, response status, console errors, failed requests and the duration of launch, navigation, readiness, capture and shutdown.
Recommended Free Tools
1. Confirm Chromium exists in the deployed runtime
Check the production installation, not your workstation
Run the diagnostic in the final container or deployed instance. Verify that Puppeteer is present in the production dependency set and inspect its install output. Package-manager policies can skip install scripts, leaving the Node package present but no browser downloaded. Install the browser revision expected by the installed Puppeteer release, then confirm the runtime user can read and execute it.
#1 Best Overall
Do not assume that an arbitrary system Chromium binary is compatible with every Puppeteer release. Browser and Puppeteer versions, as well as their native dependencies, must match the supported combination for your image.
Make the cache location explicit
In restricted environments, the default home-directory cache may not exist or may not be writable. Configure PUPPETEER_CACHE_DIR to a persistent, readable project or image path, or install the browser during image creation. At runtime, print the resolved executable path and test it with the same non-root user that handles requests.
Minimal launch probe
const puppeteer = require('puppeteer');
(async () => {
console.log('Puppeteer:', require('puppeteer/package.json').version);
console.log('Executable:', puppeteer.executablePath());
const browser = await puppeteer.launch({
headless: true,
dumpio: true,
timeout: 30_000
});
console.log('Browser launched');
await browser.close();
})();
If this probe fails, do not troubleshoot CSS or PDF options yet. Fix installation, executable permissions or native dependencies first.
Free tools Windows power users keep installed
One-click scans. No signup required.
2. Install the operating-system dependencies and fonts
Use the dependency list for your exact image
Minimal Linux images often omit shared libraries that Chromium needs. Alpine is a frequent source of confusion: Chrome does not support Alpine out of the box, and a compatible browser/Puppeteer combination requires the image’s documented system dependencies. Avoid copying a package list from an unrelated distribution or browser build; use the dependency instructions for the specific supported image and revision.
Check writable directories
Chromium may need to write a profile, temporary files, a cache and your output file. Set explicit directories and test them as the deployed user. A read-only home directory can look like a browser crash even when the executable is correct.
Diagnose missing glyphs and webfonts
Blank squares or substituted characters can mean either a CSS font-family error or an absent runtime font. Compare the result with a known installed font, inspect browser console messages and failed network requests for webfont loading errors, and package the required fonts where their licenses permit. Chinese, Japanese and Korean text commonly needs additional font files in a minimal image. A font installed on your laptop is not automatically available in production.
Rank #2
3. Treat sandbox errors as host-configuration problems
Understand “No usable sandbox!”
That message means Chrome cannot find a usable sandbox under the current host restrictions. User-namespace or AppArmor policies on some Ubuntu and container setups can prevent sandbox startup. Check kernel and container policy, user permissions and whether the process is running with the expected identity.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Puppeteer’s guidance is explicit: “Running without a sandbox is strongly discouraged. Consider configuring a sandbox instead.” Correct the host or container configuration whenever possible.
When a reduced-isolation flag is considered
--no-sandbox is not a general fix. Only consider reduced isolation when you understand the content being loaded, the threat model and the environment owner has accepted the security trade-off. Do not silently add the flag to production code merely because it makes a local test pass.
4. Separate navigation completion from application readiness
Choose a navigation condition deliberately
A successful navigation event only says that a document navigation completed; it does not prove that a client-rendered application has finished fetching data or loading fonts. Puppeteer examples commonly use waitUntil: 'networkidle2', but that is an example rather than a universal setting. Polling, streaming and analytics connections can keep a page from becoming idle, while an application can appear idle before its important DOM is populated.
Wait for the state you actually capture
const response = await page.goto('https://example.com/report', {
waitUntil: 'domcontentloaded',
timeout: 30_000
});
if (!response || !response.ok()) {
throw new Error(`Navigation failed: ${response && response.status()}`);
}
await page.waitForSelector('[data-render-complete="true"]', {
timeout: 20_000
});
await page.screenshot({path: '/tmp/report.png', fullPage: true});
If the application has no readiness marker, use a bounded delay only as a last resort and make it long enough for the target environment. Before increasing a timeout, inspect the final URL, HTTP status, console errors, failed requests and the expected DOM element. In Playwright, page APIs also expose configurable timeouts and cancellation through abort signals; cancellation does not remove the operation’s timeout, so configure both intentionally.
Keep stage-specific logs
Log launch, navigation, readiness, screenshot/PDF creation and browser shutdown separately. This distinguishes a slow server response from a browser crash, a missing selector, or a write failure.
Rank #3
5. Configure PDF output instead of relying on defaults
Print versus screen media
Puppeteer’s PDF method uses the print CSS media type by default. If the design is intended for screens, call page.emulateMediaType('screen') before generating the PDF. If it is intended for print, keep print media and define print-specific CSS deliberately.
Colors, backgrounds and page size
PDF generation modifies colors for printing by default. For exact colors, use CSS such as -webkit-print-color-adjust: exact where appropriate. The PDF option printBackground defaults to false in the documented API, so set it to true when background fills or images are required. preferCSSPageSize gives an @page rule priority over explicit dimensions; use one clear sizing strategy rather than conflicting values.
Fonts and timeout
The documented PDF options include waitForFonts: true by default and a 30,000 ms timeout. These defaults are API-version-sensitive: verify the version installed in your image before attributing a production result to a default.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →await page.emulateMediaType('screen');
await page.pdf({
path: '/tmp/invoice.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
waitForFonts: true,
timeout: 30_000
});
For a screen layout that must preserve color:
<style>
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
@page { size: A4; margin: 14mm; }
</style>
6. A complete, bounded Node.js capture example
This example checks navigation, waits for an application marker, emulates screen media and writes a PDF. Replace the URL and selector with your application’s values.
const puppeteer = require('puppeteer');
async function render() {
const browser = await puppeteer.launch({
headless: true,
timeout: 30_000,
// Do not add --no-sandbox unless your security owner has approved it.
args: []
});
try {
const page = await browser.newPage();
page.setDefaultNavigationTimeout(30_000);
page.setDefaultTimeout(20_000);
page.on('console', msg => console.error('[browser]', msg.type(), msg.text()));
page.on('requestfailed', req => console.error('[request failed]', req.url(), req.failure()));
const response = await page.goto('https://example.com/report', {
waitUntil: 'domcontentloaded'
});
if (!response || !response.ok()) {
throw new Error(`HTTP navigation status: ${response && response.status()}`);
}
await page.waitForSelector('[data-render-complete="true"]');
await page.emulateMediaType('screen');
await page.pdf({
path: '/tmp/report.pdf',
format: 'A4',
printBackground: true,
preferCSSPageSize: true,
waitForFonts: true,
timeout: 30_000
});
} finally {
await browser.close();
}
}
render().catch(error => {
console.error(error);
process.exitCode = 1;
});
7. Hosting-platform failures
Containers and serverless runtimes
Cloud Run’s Puppeteer guidance notes that the default Node.js runtime does not include every system package required by Headless Chrome; a custom Dockerfile may be necessary. Build the browser and native dependencies into the image, then test the exact deployed image rather than a local base image.
CPU after the HTTP response
Cloud Run can disable CPU after an HTTP response is written. Rendering started after sending the response can therefore become extremely slow or stop progressing. Complete synchronous rendering before responding, or configure CPU behavior to match a background-job design. Recheck the current platform settings because managed-runtime behavior can change.
Rank #4
Concurrency and memory
There is no universal safe browser-pool size or memory limit. Measure launch time, peak memory, queue delay and failure rate in your target deployment. Keep a browser alive only when your lifecycle and isolation model support it; otherwise, make sure every request closes its page and browser on success and failure.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 118. Troubleshooting by symptom
| Symptom | Likely cause | Action |
|---|---|---|
| Could not find Chrome or executable missing | Browser download skipped, wrong cache path or absent production dependency | Inspect install logs, set PUPPETEER_CACHE_DIR, install the expected revision and print executablePath(). |
| No usable sandbox | Host namespace, AppArmor, container or permission restriction | Fix sandbox support and permissions; treat --no-sandbox only as an explicitly accepted security exception. |
| Browser exits immediately | Missing shared library, incompatible browser build, unwritable profile or temporary directory | Run the launch probe with dumpio, verify native dependencies and writable paths as the runtime user. |
| Blank screenshot | Wrong URL, failed request, client app not ready, blocked resources or an unexpected redirect | Log final URL/status, console and request failures; wait for a real DOM readiness marker. |
| Screenshot times out | Long-lived requests, polling, slow server response or an overly strict idle condition | Use a bounded navigation timeout and an application-specific selector/state instead of extending time blindly. |
| PDF lacks colors or backgrounds | Print media and printBackground: false |
Choose screen or print media deliberately, set printBackground: true, and use print-color adjustment CSS when exact color matters. |
| Missing or substituted glyphs | Font absent in the image or webfont request failed | Install licensed runtime fonts, verify CSS family names and inspect font requests. |
| Works locally but is slow on Cloud Run | Missing image dependencies or CPU disabled after response | Use a custom image and finish rendering before sending the response, or configure background CPU behavior. |
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server for developers. One GET request returns PNG, JPEG, WebP or PDF. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status.
For a direct call, 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)
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}`);
ScreenshotNeo also offers full-page lazy-image loading, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper/margin/landscape/page-range controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request/resource blocking, headers, cookies, user-agent, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of 100 URLs per call, a usage API and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
Every plan includes every feature. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
Cost, reliability and security decisions
- Measure in the target image instead of publishing a supposedly universal concurrency or memory number; browser builds and pages vary.
- Use bounded timeouts and always close pages and browsers in a
finallyblock. - Keep credentials, cookies and authorization headers out of logs and screenshots.
- Restrict outbound destinations if users can submit URLs; a rendering service can otherwise become a server-side request-forgery path.
- Cache only when stale content is acceptable, and include the relevant URL, viewport, media and authentication context in your cache key.
- For PDFs, make paper size, margins, media type, backgrounds and font availability explicit so upgrades do not silently change output.
FAQ
Should I switch from Puppeteer to Playwright?
Switching may change API controls, packaging and cancellation behavior, but it does not automatically install missing OS libraries or repair a host sandbox. First identify whether the failure is installation, launch, readiness or output.
Is networkidle2 always the right wait condition?
No. It is useful for some pages, but polling, streaming and delayed application state can make it misleading. A selector or explicit readiness state tied to the content you capture is usually more meaningful.
Why does a PDF differ from the browser view?
PDF generation uses print media unless you emulate screen media, and print color/background settings differ from a normal screenshot. Define those choices explicitly and verify fonts in the runtime image.
Can I solve all server rendering failures with a larger timeout?
No. A timeout cannot install Chromium, add a missing font, enable a sandbox or make a failed application request succeed. Use logs to locate the stage first.
Frequently Asked Questions
Should I switch from Puppeteer to Playwright?
Switching may change API controls, packaging and cancellation behavior, but it does not automatically install missing OS libraries or repair a host sandbox. First identify whether the failure is installation, launch, readiness or output.
Is networkidle2 always the right wait condition?
No. It is useful for some pages, but polling, streaming and delayed application state can make it misleading. A selector or explicit readiness state tied to the content you capture is usually more meaningful.
Why does a PDF differ from the browser view?
PDF generation uses print media unless you emulate screen media, and print color/background settings differ from a normal screenshot. Define those choices explicitly and verify fonts in the runtime image.
Can I solve all server rendering failures with a larger timeout?
No. A timeout cannot install Chromium, add a missing font, enable a sandbox or make a failed application request succeed. Use logs to locate the stage first.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →The Bottom Line
Fix the layer that is failing: install a compatible browser and native dependencies, preserve sandbox and writable paths, wait for application readiness, configure PDF media and colors, and match the hosting lifecycle to the render job. If maintaining Chromium in your server is the problem, ScreenshotNeo provides the capture endpoint and MCP tools without requiring your runtime to launch a browser.
Quick Recap
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.

