Chrome flags (also called switches) are command-line arguments such as --headless or --window-size=1280,800 that change how the Chrome process behaves for its whole lifetime, set once when the browser starts.
They are different from per-page settings. A flag applies to every tab in that browser process, and you cannot change it without a restart. This entry is part of the screenshot glossary.
What they are in plain terms
When Puppeteer or Playwright "launches Chrome", it runs the Chrome executable with a long list of flags. Some switch off background work you do not want in automation (updates, sync, the first-run screen). Some open the control channel, the Chrome DevTools Protocol. And you can add your own.
We printed the real command line from both libraries. Puppeteer 24.43.1 adds 33 flags before yours. Playwright 1.64.1 started its browser with 46. Two of them matter most for screenshots:
| Library | Binary it starts by default | Headless flag | Sandbox |
|---|---|---|---|
| Puppeteer 24 | Chrome for Testing (chrome) |
--headless=new |
On (no --no-sandbox) |
| Playwright 1.64 | chrome-headless-shell |
--headless |
Off (--no-sandbox added) |
So the "same" headless screenshot runs on different binaries with different security settings, depending on the library. See chrome-headless-shell and the Chromium sandbox for what that means.
Where flags sit
flowchart TB A["Process flags<br/>--headless, --window-size, --force-device-scale-factor, --no-sandbox"] --> B[Browser process] B --> C["Browser context<br/>cookies, permissions, proxy"] C --> D["Page settings via CDP<br/>viewport, locale, media, user agent"] D --> E[Screenshot]
A flag is the outermost layer. Anything a library can set per page (viewport, locale, color scheme) is the better choice, because one browser can then serve many jobs with different settings.
Flags that affect screenshots
| Flag | Effect | Per-page alternative |
|---|---|---|
--headless / --headless=new |
Run without a window | none |
--window-size=1280,800 |
Initial window size | page.setViewport / viewport option |
--force-device-scale-factor=2 |
Retina-style 2x pixels | deviceScaleFactor |
--hide-scrollbars |
No scrollbar in captures | Puppeteer adds it by default |
--force-color-profile=srgb |
Stable colors across machines | Puppeteer adds it by default |
--disable-dev-shm-usage |
Use /tmp instead of a small /dev/shm in Docker |
none |
--no-sandbox |
Turn off process isolation | Avoid; see the sandbox entry |
--remote-debugging-port=9222 |
Open the CDP port | none |
--lang=de-DE |
UI language | Accept-Language header and locale emulation |
Setting flags in Puppeteer, Playwright and RenderScreenshot
Puppeteer takes extra flags in args, and you can read back what Chrome really received:
import puppeteer from 'puppeteer'; console.log(`${puppeteer.defaultArgs({ headless: true }).length} default flags`); const browser = await puppeteer.launch({ args: ['--window-size=1280,800', '--force-device-scale-factor=2', '--lang=de-DE'], }); try { const spawned = browser.process().spawnargs.slice(1); console.log(spawned.filter((f) => /window-size|scale-factor|lang|headless/.test(f))); const page = await browser.newPage(); await page.setViewport(null); // keep the window size instead of Puppeteer's 800x600 default await page.goto('https://example.com'); console.log(await page.evaluate(() => ({ dpr: devicePixelRatio, width: innerWidth, languages: navigator.languages, }))); } finally { await browser.close(); }
Output: 33 default flags, then { dpr: 2, width: 1280, languages: [ 'en-US' ] }. The scale and window flags worked. --lang=de-DE did not change navigator.languages in headless mode on our machine. For localized captures, set the locale per page instead.
Playwright has the same args option. To see the full command line, start a browser server, which exposes the child process:
import { chromium } from 'playwright'; const server = await chromium.launchServer({ args: ['--force-device-scale-factor=2'] }); try { const spawned = server.process().spawnargs.slice(1); console.log(`${spawned.length} flags`); console.log(spawned.filter((f) => /headless|sandbox|scale-factor/.test(f))); } finally { await server.close(); }
Output: 46 flags and [ '--headless', '--no-sandbox', '--force-device-scale-factor=2' ]. For scale, locale and viewport, Playwright's newContext({ deviceScaleFactor, locale, viewport }) is the cleaner route.
With RenderScreenshot you do not pass flags. Each request gets a fresh browser session, and the per-capture settings are request parameters:
curl -G https://api.renderscreenshot.com/v1/screenshot \ -H "Authorization: Bearer $RS_API_KEY" \ --data-urlencode "url=https://example.com" \ -d width=1280 -d height=800 -d scale=2 -d locale=de-DE \ --output [email protected]
scale maps to viewport.scale and locale to browser.locale, which also sets the Accept-Language header. See the viewport and browser options.
Tested on 2026-10-15 on macOS 27 (arm64) with Node 22.18.0, [email protected] (Chrome 148.0.7778.97) and [email protected] (chrome-headless-shell 156.0.8078.4). The curl request was not run for this page.
Common mistakes
- Copying flag lists from old blog posts.
--disable-gpuwas a workaround for early headless builds.--single-processand--no-zygotemake Chrome less stable. Add a flag only when you can name the problem it fixes. - Using flags for per-job settings. Locale, viewport and scale belong on the page or context, so one browser can serve every job. See scaling Puppeteer screenshots.
- Adding
--no-sandboxby reflex. It is often needed only because Chrome runs as root in a container. Fix the user instead. - Forgetting that flags change between versions. Switches are not a stable API. Re-check them when you upgrade the browser.
Related terms
To capture pages without managing a Chrome command line, create a free account.