The Complete Guide to Headless Browsers
Headless browsers power everything from automated testing to web scraping to screenshot generation. This guide covers what they are, how the three major tools compare, and when you should skip the browser entirely and use a managed API instead.
What is a headless browser?
A headless browser is a web browser that runs without a visible window. It loads pages, executes JavaScript, applies CSS, fires network requests — everything a normal browser does — but it's controlled by code instead of a human clicking around.
Under the hood, it's the same rendering engine. Chrome's headless mode uses the same Blink engine and V8 JavaScript runtime as the Chrome on your desktop. The only difference is that pixels aren't painted to a screen — they're captured to memory, files, or API responses.
Common use cases:
- -Automated testing — end-to-end UI tests in CI/CD pipelines
- -Web scraping — extracting data from JavaScript-rendered pages
- -Screenshot generation — capturing pages as PNG/JPEG images
- -PDF generation — converting web pages to print-ready PDFs
- -Performance monitoring — measuring load times and Core Web Vitals
- -SEO auditing — checking rendered output for search engines
- -Form automation — filling and submitting forms programmatically
Puppeteer vs Playwright vs Selenium
Three tools dominate headless browser automation. Here's how they compare on the things that actually matter in production.
| Feature | Puppeteer | Playwright | Selenium |
|---|---|---|---|
| Maintainer | Microsoft | Community (SeleniumHQ) | |
| Languages | Node.js, Python | Node.js, Python, Java, .NET | Java, Python, C#, Ruby, JS |
| Browsers | Chrome/Chromium only | Chromium, Firefox, WebKit | Chrome, Firefox, Safari, Edge |
| Auto-waiting | Manual waits needed | Built-in auto-wait | Manual waits needed |
| Full-page screenshot | Yes | Yes | Viewport only (hacks needed) |
| PDF generation | Yes | Yes (Chromium only) | No |
| Network interception | Yes | Yes (better API) | Limited |
| Parallel execution | Manual | Built-in contexts | Selenium Grid |
| Binary size | ~170MB | ~400MB (multi-browser) | ~0 (uses system browser) |
| Learning curve | Low | Low-Medium | Medium-High |
| Best for | Chrome-only automation | New projects in 2026 | Legacy test suites |
Puppeteer
Google's official Node.js API for controlling Chrome. Puppeteer downloads a compatible Chromium binary automatically, giving you full browser control with a clean async/await API. It's the most widely used headless browser tool — most tutorials and Stack Overflow answers reference Puppeteer.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
// Screenshot
await page.screenshot({ path: 'page.png', fullPage: true });
// Extract data
const title = await page.$eval('h1', el => el.textContent);
console.log(title);
// Generate PDF
await page.pdf({ path: 'page.pdf', format: 'A4' });
await browser.close();
})();Production gotchas:
- Chrome processes leak memory in long-running scripts — restart browsers periodically
- No auto-waiting — you need explicit
waitForSelectororwaitForNetworkIdlecalls - ~170MB Chromium download on first run — problematic in CI/CD and Docker
- Chrome-only — no Firefox or Safari testing
Playwright
Microsoft's modern answer to Puppeteer. Built by the same engineers who originally created Puppeteer at Google, then moved to Microsoft. Playwright fixes most of Puppeteer's pain points: auto-waiting, multi-browser support, better parallelism via browser contexts, and first-class TypeScript support.
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// Auto-waits for content — no manual waitUntil needed
await page.screenshot({ path: 'page.png', fullPage: true });
// Extract data with locators
const title = await page.locator('h1').textContent();
console.log(title);
// Generate PDF
await page.pdf({ path: 'page.pdf', format: 'A4' });
await browser.close();
})();Why Playwright wins for new projects:
- Auto-waiting eliminates flaky tests and race conditions
- Browser contexts enable true parallel execution without separate processes
- Locator API is more resilient than CSS selectors alone
- Built-in test runner with HTML reporting and trace viewer
Selenium
The original browser automation framework, dating back to 2004. Selenium supports the most languages and browsers of any tool on this list. It's battle-tested in enterprise environments — but the API shows its age. Most teams starting fresh in 2026 should choose Playwright instead.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1280,800")
driver = webdriver.Chrome(options=options)
driver.get("https://example.com")
# Screenshot
driver.save_screenshot("page.png")
# Extract data
title = driver.find_element("css selector", "h1").text
print(title)
driver.quit()When Selenium still makes sense:
- Existing test suites with thousands of Selenium tests
- Teams using Java, C#, or Ruby (Playwright's support is weaker here)
- Need for Selenium Grid distributed testing infrastructure
- Safari testing on real macOS hardware
Self-hosted vs managed APIs
Running your own headless browser gives you full control. Using a managed API gives you zero infrastructure. Here's when each approach makes sense.
Self-host when:
- -You need full browser control (custom scripts, extensions)
- -Running thousands of operations daily
- -You have dedicated DevOps capacity
- -Data must stay on your infrastructure
- -You need to run end-to-end tests
Use a managed API when:
- -You want zero infrastructure to maintain
- -Running in serverless (Lambda, Cloud Functions)
- -You need Cloudflare/anti-bot bypass
- -Volume is low-to-medium (under 10K/day)
- -You need screenshots, PDFs, and scraping in one tool
SnapRender: the managed alternative
If you need screenshots, PDFs, HTML rendering, or markdown extraction — but don't want to manage browsers — SnapRender wraps headless Chromium behind a simple REST API. No binaries to install, no processes to manage, no Cloudflare to fight.
// Screenshot — one API call, no browser
const screenshot = await fetch('https://api.snaprender.dev/v1/screenshot', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: 'https://example.com', full_page: true })
});
// PDF — same API, different endpoint
const pdf = await fetch('https://api.snaprender.dev/v1/pdf', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: 'https://example.com' })
});
// Markdown extraction — same API
const markdown = await fetch('https://api.snaprender.dev/v1/markdown', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: 'https://example.com' })
});One API key, three endpoints, zero browser management. 100 free requests per month.
The hidden costs of self-hosting
"Free" headless browsers aren't free. Here's what you actually pay for when you run Puppeteer or Playwright in production:
| Cost | Reality |
|---|---|
| Server infrastructure | 2GB+ RAM minimum for Chrome. $20-50/mo for a VPS that can handle concurrency. |
| Memory management | Chrome leaks memory. You need process recycling, OOM handlers, and monitoring. |
| Browser updates | Chromium updates can break scripts. Someone needs to test and fix after each update. |
| Scaling | Each browser instance uses 200-500MB RAM. Scaling to 10 concurrent = 2-5GB RAM. |
| Cloudflare/anti-bot | Headless Chrome is detected by default. Evasion requires stealth plugins and maintenance. |
| On-call debugging | When Chrome crashes at 3am, someone needs to restart it. Or page you. |
Skip the infrastructure
Screenshots, PDFs, scraping, and markdown extraction — all from a single API. 100 free requests per month. No browser to install. No credit card.
Get your API keyFrequently asked questions
A headless browser is a web browser without a visible user interface. It runs in the background, loading pages, executing JavaScript, and rendering CSS just like a regular browser — but controlled programmatically via code instead of mouse and keyboard.
Common use cases include: automated testing (end-to-end UI tests), web scraping (extracting data from JavaScript-rendered pages), screenshot/PDF generation, performance monitoring, and SEO auditing. Any task that requires a real browser but no human interaction.
Playwright is generally better for new projects in 2026. It has auto-waiting, multi-browser support, better TypeScript integration, and a more modern API. Puppeteer is still solid if you only need Chrome and want the larger ecosystem of community examples.
Standard headless browsers (Puppeteer, Playwright, Selenium) are detected and blocked by Cloudflare's bot detection. Bypassing requires specialized tools like FlareSolverr, undetected-chromedriver, or managed APIs like SnapRender that handle bypass automatically.
Self-host if you need full browser control, have DevOps capacity, and run thousands of operations daily. Use a managed API if you want zero infrastructure, need Cloudflare bypass, or run in serverless environments where browser cold starts are a problem.