ScreenshotNeo

BlogHow-to

How to take a Puppeteer screenshot of a web page hosted on localhost in Windows

Run Puppeteer on Windows to capture a local web page. Set up the browser, choose what to capture, and fix common launch and rendering issues.

By the ScreenshotNeo team4 October 20267 min read

To take a Puppeteer screenshot of a page hosted on localhost in Windows, start your local web server, navigate Puppeteer to its complete local URL, and save the screenshot with page.screenshot(). Use the Puppeteer-managed browser where possible, and choose a wait condition that matches how your app renders.

1. Start the local page

Run your app’s development server in a terminal and leave it running. Find its local address and port, such as http://localhost:3000. Replace the port and route in the examples below with the address your app prints. Include http:// or https://; Puppeteer expects a URL with a scheme.

The Node process running Puppeteer must be able to reach that address. If the server binds only to a specific interface, uses a different port, or has not finished starting, navigation can fail even if the screenshot script itself is valid.

2. Install Puppeteer and capture the page

In your project directory, install Puppeteer:

npm install puppeteer

Puppeteer normally manages a compatible browser for its installed version. Use the Node version required by that version of Puppeteer. The current documentation in the research for this guide lists Node 22.12+ for Puppeteer 25.12.0, but requirements can change; check the official system requirements for your installed release.

Create screenshot.js:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    const response = await page.goto('http://localhost:3000', {
      waitUntil: 'networkidle2',
    });

    if (response && !response.ok()) {
      throw new Error(`Local page returned HTTP ${response.status()}`);
    }

    await page.screenshot({ path: 'localhost.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Run it from the project directory:

node screenshot.js

The example navigates to the local app, checks an available HTTP response for a successful status, captures the full page, and closes the browser even if navigation or capture throws. A relative output path such as localhost.png is resolved from the process’s current working directory, which is usually the directory from which you ran node.

The Puppeteer API documents page.goto() for navigation and page.screenshot() for capture. The official screenshot guide shows the launch, page creation, navigation, capture, and close workflow.

3. Choose the capture and wait behavior

Need Option Behavior
Only the visible viewport Omit fullPage or set it to false The default is a viewport screenshot.
The whole document fullPage: true Requests a full-page capture. Pages with very large dimensions may take longer and produce larger files.
A rectangle of the page clip screenshot option Captures the specified region. Set its coordinates and dimensions for the layout you need.
One element Find the element and call its screenshot method Useful for a component or chart without capturing the rest of the page.
Image bytes in memory Call page.screenshot() without path Returns screenshot data as a Uint8Array; write or process those bytes yourself.
A file on disk Pass path Puppeteer writes the screenshot to that path.

See Puppeteer’s screenshot options for the supported options and their current details.

Wait for the right signal

networkidle2 is used in the example and can suit pages that become quiet after loading. Some apps keep network requests open, poll, or load content after navigation. In those cases, use a different navigation wait condition or wait for an app-specific selector before capturing. For example:

await page.goto('http://localhost:3000', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-ready="true"]');
await page.screenshot({ path: 'localhost.png', fullPage: true });

Replace the selector with an element or state your app sets only after the content you need is ready. A fixed delay can be used when an app has no reliable readiness signal, but it adds time and may still be too short or unnecessarily long.

4. Windows browser setup choices

Prefer the browser managed for your Puppeteer version. Puppeteer documents that it guarantees compatibility with its bundled browser; using a system Chrome installation is supported through launch options, but compatibility is not guaranteed in the same way.

If you need to select a browser channel or executable, Puppeteer provides launch options such as channel and executablePath. Use the path to an installed browser only when your environment requires it, and check it against the installed Puppeteer version. Browser management documentation also describes installing Chrome for Testing with npx @puppeteer/browsers install chrome@stable.

On Windows, the current system requirements surfaced in the research list Windows x64 for Chrome for Testing and mention tar.exe or PowerShell for unpacking browser binaries in the documented setup. These are version-sensitive details; consult the system requirements for your release.

5. Troubleshooting

Symptom Likely cause What to check or change
Navigation reports connection refused or times out The local server is stopped, the port or route is wrong, or the server is not reachable from the Node process. Start the app first; open the same scheme, host, port, and route in a browser; update the URL in the script.
Browser executable cannot be found The managed browser is missing, or Puppeteer is looking in a different cache location. Follow Puppeteer’s browser installation guidance and check its configured cache. The troubleshooting guide documents PUPPETEER_CACHE_DIR for changing the cache directory.
Chrome launch fails under Windows policy Some managed Chrome policies that enforce extensions can conflict with Puppeteer’s default launch behavior, which disables extensions. Check the organization’s browser policy and Puppeteer’s troubleshooting guidance. It documents enableExtensions as a possible workaround where appropriate.
Permission error when starting downloaded Chrome Downloaded browser file permissions may not be configured for the environment or Puppeteer version. Use the version-specific troubleshooting instructions. Puppeteer says versions starting at v22.14.0 attempt to configure downloaded Chrome permissions with Chrome’s setup.exe; older versions or persistent errors may require the documented manual steps.
Chrome cannot write its profile The user-data directory is not writable. Choose a writable userDataDir in launch options and ensure the running account can access it.
Screenshot file is missing No path was supplied, or the relative path resolved somewhere unexpected. Pass a path and check the current working directory. Use an absolute output path if the script is launched from varying directories.
Screenshot is blank or content is missing The app has not rendered the needed content, or the selected wait condition finished too early. Wait for a meaningful selector or app readiness state before capture. Confirm the page works at the exact local URL.
Screenshot is much taller than expected fullPage is enabled. Remove it or set fullPage: false to capture the viewport.
HTTP error despite a generated screenshot The server returned an error page with an HTTP status instead of the expected content. Inspect the response status and server logs. The example throws on a non-success response so this does not pass silently.

For the exact version and environment, use Puppeteer’s troubleshooting guide, configuration documentation, and launch options.

6. Performance, reliability, and cost

  • Keep one browser open for a batch. Launching a browser has setup overhead. If capturing several routes in one script, reuse the browser and create pages as needed, then close it in a finally block.
  • Wait only as long as the page needs. Network-idle waits can hang or delay on pages with persistent requests. A selector that indicates the target content is ready is often more predictable for app-specific rendering.
  • Make output paths explicit in automation. Working directories vary between terminals, IDEs, and scheduled jobs; an absolute path or a path built from the script directory avoids confusion.
  • Keep browser versions aligned. Puppeteer’s bundled browser is the compatibility baseline. A custom Chrome executable can introduce version-specific behavior.
  • Account for local resources. Fonts, images, API calls, and other assets must also be reachable by the browser. A page may load while some resources fail.
  • Cost. Puppeteer is an open-source browser automation library; this local workflow has no per-screenshot ScreenshotNeo charge. You still need compute, storage, and maintenance for the Windows machine or automation environment running the browser.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request takes a URL and returns an image or PDF. For a localhost page, a hosted capture service cannot reach your machine’s private loopback address directly; expose a reachable development or staging URL first, and avoid exposing sensitive local content.

For a reachable page, the same call pattern works from cURL, Python, or Node.js. See the ScreenshotNeo API documentation for parameters and response handling.

cURL

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is available on every plan.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Can Puppeteer capture a page on localhost?

Yes. Navigate to the local URL from the Node process on the same machine or an environment that can reach the server. The app must be running and the URL must include its scheme and port.

Why does localhost work in my browser but fail in Puppeteer?

The script may be using a different port, route, scheme, or execution environment. Check the exact URL and confirm the server is reachable from the process that launches Puppeteer.

Does the screenshot save beside screenshot.js?

Only if that is the process’s current working directory. Relative paths resolve from the current working directory, not necessarily the script’s folder.

Can ScreenshotNeo capture my localhost URL directly?

No, a hosted service cannot access your machine’s private localhost address. Make the page reachable at a suitable URL first, then use the API call above.