ScreenshotNeo

BlogGuides

Puppeteer goto() Options: How to Control Page Navigation

Choose Puppeteer’s navigation wait condition, timeout, cancellation signal, and referrer settings. Learn what goto() returns and how to handle common failures.

By the ScreenshotNeo team4 October 20267 min read

page.goto(url, options) navigates a Puppeteer page or frame. Set waitUntil to choose the browser lifecycle milestone to wait for, timeout to set the time budget, signal to cancel the wait, and referer or referrerPolicy to control referrer metadata. The default completion condition is 'load', and the default timeout is 30,000 ms. A resolved response does not necessarily mean the HTTP status is successful, and a lifecycle event does not prove that an application has finished rendering its data.

This guide follows the Puppeteer API documentation labeled v25.12.0. Check the documentation for the version installed in your project if exact behavior matters.

1. A minimal navigation with status handling

Use a fully qualified URL, including its scheme. Inspect the returned response if your workflow requires a successful HTTP status:

const response = await page.goto('https://example.com', {
  waitUntil: 'domcontentloaded',
  timeout: 15_000,
});

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

await page.waitForSelector('main article', { visible: true });

The selector is an example; choose one that represents readiness in the target application. The code uses an earlier lifecycle milestone and then waits for the UI the next step actually needs.

2. Choose the right waitUntil condition

waitUntil controls when Puppeteer considers navigation waiting complete. It accepts one lifecycle event or an array of events. For an array, every listed event must fire before the navigation wait completes. The default is 'load'.

Value What it waits for Good fit
'commit' The navigation response has been received and the document has started loading. When you need to know navigation began and will perform a separate readiness wait.
'domcontentloaded' The document’s DOM content has been loaded and parsed. When scripts or a selector-based wait will handle the remaining application readiness.
'load' The window load event fires. This is the default. When the page’s load event is an appropriate milestone for the next operation.
'networkidle0' No more than zero network connections remain for at least 500 ms. When network quiet is meaningful for the page and its background requests settle.
'networkidle2' No more than two network connections remain for at least 500 ms. When a small amount of continuing network activity is expected.

These are browser milestones, not guarantees that a single-page application has loaded its data, finished client-side rendering, or become usable. Pages with polling, analytics, streaming, or long-lived requests may never become network-idle. Select the earliest milestone that supports the next step, then wait for an application-specific selector or condition if needed.

// Wait for DOM parsing, then wait for the control the script needs.
await page.goto('https://example.com/dashboard', {
  waitUntil: 'domcontentloaded',
});
await page.waitForSelector('[data-testid="dashboard-ready"]', {
  visible: true,
  timeout: 10_000,
});

3. Set a timeout and cancellation signal

The per-navigation timeout is in milliseconds. It defaults to 30,000; 0 disables the timeout. Use a finite timeout for unattended work so a navigation cannot wait forever. Choose a value that accounts for the target and your overall job deadline.

// Per-call timeout
await page.goto('https://example.com', {
  waitUntil: 'load',
  timeout: 20_000,
});

// Disable this navigation timeout (use only when another control bounds the job)
await page.goto('https://example.com', { timeout: 0 });

You can set a default for a page. setDefaultNavigationTimeout() applies to navigation operations including goto(), goBack(), goForward(), reload(), setContent(), and waitForNavigation(). The general setDefaultTimeout() also changes the default timeout used by waits. A timeout passed to an individual call makes that call’s budget explicit.

page.setDefaultNavigationTimeout(20_000);
// Or set the general default timeout for waits as well:
page.setDefaultTimeout(15_000);

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });

signal accepts an AbortSignal. This is useful when a surrounding task has a cancellation path or deadline:

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 12_000);

try {
  await page.goto('https://example.com', {
    waitUntil: 'domcontentloaded',
    timeout: 20_000,
    signal: controller.signal,
  });
} finally {
  clearTimeout(timer);
}

The example bounds the wait with both a navigation timeout and a cancellation signal. Handle abort and timeout errors at the job boundary according to whether retrying is appropriate.

4. Set referrer information for one navigation

GoToOptions includes referer and referrerPolicy. These per-navigation values take precedence over the corresponding referrer headers set with page.setExtraHTTPHeaders().

await page.goto('https://example.com/destination', {
  waitUntil: 'domcontentloaded',
  referer: 'https://example.com/source',
  referrerPolicy: 'strict-origin-when-cross-origin',
});

Only set referrer metadata when the target workflow requires it. A server may use request headers for routing or access decisions; if the destination behaves differently than expected, inspect the actual request and the server response.

5. Understand the return value

goto() resolves to the main resource’s HTTPResponse. If redirects occur, the response is for the final redirect destination. A response can have an unsuccessful HTTP status such as 404 or 500 without the call rejecting; check response.status() or response.ok() when status matters.

const response = await page.goto('https://example.com/missing', {
  waitUntil: 'load',
});

if (response === null) {
  console.log('Navigation completed without an HTTP response object');
} else {
  console.log('Final URL:', response.url());
  console.log('HTTP status:', response.status());
  console.log('HTTP success:', response.ok());
}

Navigation to about:blank and same-URL navigation that changes only the URL hash can resolve with null. Do not dereference the response without checking it.

6. Avoid races when an action triggers navigation

Start waiting for navigation before performing the click or action. If the click happens first, a fast navigation can begin before the wait is registered:

const [response] = await Promise.all([
  page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
  page.click('a.next'),
]);

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

For a client-side action that does not cause a document navigation, use a selector or application condition instead of waiting for navigation. The wait must match the behavior of the action.

7. Common errors and fixes

Symptom Likely cause What to do
Navigation timeout The chosen lifecycle condition did not occur before the time budget. Network idle may be unsuitable for pages with ongoing requests. Choose an earlier milestone such as 'domcontentloaded', then wait for a specific UI condition. Increase the timeout only when the page legitimately needs more time.
Invalid URL or navigation failure The target is malformed or lacks a scheme, or the main resource could not load. Pass a complete URL such as https://example.com and verify the destination is reachable from the browser environment.
SSL error The certificate is invalid, expired, or untrusted, including some self-signed certificates. Fix the certificate or trust configuration in the environment. Avoid treating certificate bypass as a routine production fix.
Navigation promise resolves but the script fails on status An HTTP response such as 404 or 500 is still a response; it is not necessarily a rejected navigation. Check for a null response, then inspect status() or ok().
Wait for navigation hangs after a click The page action may not navigate, or the navigation wait was registered too late. Register the wait before the click with Promise.all. If it is an in-page update, wait for its resulting selector or state instead.
Navigation blocked or remote server unreachable Network, server, or configured blocklist/allowlist restrictions prevent the main resource from loading. Check connectivity, destination availability, and browser policy rules from the same runtime that launches Puppeteer.
PDF URL fails in headless shell Puppeteer’s headless shell mode does not support navigation to PDF documents. Use a supported browser mode or obtain/generate the PDF through an appropriate separate flow. This caveat is specific to headless shell.

Documented navigation failures include SSL errors, invalid URLs, timeouts, unreachable or nonresponding servers, main-resource load failures, and URLs disallowed by blocklist or allowlist rules.

8. Reliability, performance, and cost considerations

  • Pick a precise readiness signal. Waiting for the full load event or network quiet can add latency. A DOM milestone followed by a selector wait is often a clearer contract when the task needs one specific control.
  • Keep waits bounded. Use finite navigation and selector timeouts, and propagate cancellation when a larger job is stopped. A disabled timeout is safe only if another deadline bounds the task.
  • Handle partial success. A resolved response may be null or have an unsuccessful HTTP status. Validate the response and the page state required by the next step.
  • Retry selectively. A transient network failure may merit a retry; invalid URLs, disallowed destinations, persistent certificate problems, or a stable 404 usually require correcting inputs or policy. Bound retries to avoid multiplying time and load.
  • Budget the whole workflow. Navigation is only one part of browser execution. Include selector waits and subsequent work in the job deadline. This API documentation does not specify a cost benchmark; actual runtime and infrastructure cost depend on your deployment and page behavior.

9. Or skip the browser setup

If your goal is a website screenshot rather than browser automation, ScreenshotNeo provides a screenshot API and MCP server. Its API takes one GET request with a URL and returns an image or PDF. See the ScreenshotNeo API documentation.

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

10. FAQ

Can goto() navigate to a page that requires authentication?

It navigates to the URL using the browser’s current session and request context. Configure the session or request headers as your application requires, then verify the resulting response and page state.

Does waitUntil: 'networkidle0' mean the page is ready?

No. It means the specified network quiet condition occurred. It does not certify that application data is correct or that a particular control is usable.

Can I use the same options with waitForNavigation()?

It shares navigation wait controls such as lifecycle conditions and timeout behavior. For an action-triggered navigation, register that wait before the action.

Does a 404 always throw from goto()?

No. Inspect the returned response status; a valid HTTP error response can resolve normally.

Official references