Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a Playwright Page and await page.goto('https://example.com'). The call performs a direct navigation, waits for the load event by default, and returns the main-resource response when one exists. Create the browser, context, and page first; then close the context and browser after the work is complete.

Minimal direct navigation

This complete Node.js example launches Chromium, opens an isolated browser context, navigates to an absolute URL, checks the HTTP response, and closes resources in the correct order:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();

  const response = await page.goto('https://example.com');

  if (response) {
    console.log('status:', response.status());
    console.log('final URL:', page.url());
  } else {
    console.log('No main-resource response was returned.');
  }

  await context.close();
  await browser.close();
})();

The URL should normally include a scheme such as https://. If the context has a baseURL, a relative path can be resolved against it. Playwright documents goto and its return behavior in the Page API reference.

What page.goto() waits for

Navigation has several useful milestones. Select the one that matches what your code needs rather than assuming that one state is correct for every site.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

load: the default

With no options, page.goto() waits for the page’s load event. This is a practical default when scripts, stylesheets, images, and other load-event resources must be available before the next step.

commit: response accepted

commit resolves as soon as the response is received and document loading starts. It is useful when you need to begin work early, such as inspecting the destination or waiting for a separate application signal.

domcontentloaded: HTML parsed

This state resolves after the initial HTML has been parsed, without waiting for every load-event resource. It can shorten workflows that only need the document structure.

networkidle: rarely the right test signal

networkidle waits for a period with no active network connections. Modern applications may poll, stream, or load data lazily, so this state can be delayed or never represent a meaningful user-ready screen. Playwright explicitly says not to use this method as a general testing strategy; use web assertions to assess readiness instead. See the Page API and navigation guide.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.goto('https://example.com/dashboard', {
  waitUntil: 'domcontentloaded'
});

For a test, assert the outcome a user needs. For example:

const { test, expect } = require('@playwright/test');

test('dashboard opens', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  await expect(page.getByRole('heading', { name: 'Get started' })).toBeVisible();
});

The heading, role, or other assertion must match your application; the important pattern is to verify visible behavior instead of treating a lifecycle event as proof that the page is ready.

Absolute URLs, base URLs, and the current address

Use an absolute URL by default

Pass a complete URL, including https:// or http://:

await page.goto('https://www.example.com/products');

An invalid URL can cause navigation to fail. A string without a scheme is not automatically treated as a web address unless it can be resolved through a configured base URL.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Resolve paths with baseURL

Set baseURL when all routes belong to the same application:

const context = await browser.newContext({
  baseURL: 'https://example.com'
});
const page = await context.newPage();

await page.goto('/products');

This keeps test routes short while retaining a single host configuration.

Read the resulting URL

Call page.url() after navigation to obtain the address currently shown in the page:

await page.goto('https://example.com/start');
console.log(page.url());

Redirects may mean that the final URL differs from the one supplied to goto.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Navigation caused by a click or form submission

Use goto for direct navigation. A click, link activation, or form submission can trigger navigation implicitly, so coordinate the URL wait with the action. Start waiting before the action to avoid missing a fast navigation:

const urlWait = page.waitForURL('**/account');
await page.getByRole('link', { name: 'Account' }).click();
await urlWait;

console.log('arrived at:', page.url());

waitForURL accepts a glob, regular expression, URL pattern, or predicate. An un-wildcarded string is an exact URL match. You can also verify the resulting page content:

await page.waitForURL(//account(?:?|$)/);
await expect(page.getByRole('heading', { name: 'Account' })).toBeVisible();

This distinction between explicit and interaction-triggered navigation is described in the Pages guide and the Page API.

Inspecting responses, redirects, and HTTP errors

Check the returned response

page.goto() returns the response for the main resource in normal cases. Inspect its status when an HTTP success response is required:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const response = await page.goto('https://example.com/report');

if (!response) {
  throw new Error('Navigation returned no main-resource response');
}

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

A 404 or 500 response does not, by itself, make goto throw. The navigation can complete while the response reports an error status, so check status() or ok() explicitly. Playwright documents this behavior in the Page API.

Understand redirects

For server redirects, the navigation resolves with the first non-redirect response. A client-side redirect that occurs before load makes Playwright wait for the redirected page’s load event. Always use page.url() or an assertion when the destination matters.

Know when null is expected

The return value can be null for about:blank and for navigation to the same URL’s fragment. Do not treat a null response alone as a failed navigation; decide whether your workflow expected a network response.

Timeouts and failure handling

Navigation can fail for an invalid URL, SSL error, timeout, unreachable server, or failure to load the main resource. Give the operation a timeout appropriate to your environment and capture a useful error:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
try {
  await page.goto('https://example.com/slow', {
    waitUntil: 'domcontentloaded',
    timeout: 30_000
  });
} catch (error) {
  console.error('Navigation failed:', error.message);
  console.error('URL at failure:', page.url());
  throw error;
}

For a suite-wide policy, configure the navigation timeout on the page or context; use a per-call value when one route is known to need different treatment. Avoid solving every timeout by choosing networkidle: an assertion on the required heading, table, or state is usually more deterministic.

Contexts, pages, and state isolation

A Page is a tab or popup inside a BrowserContext. A context can contain multiple pages, while separate contexts isolate cookies and cache. This lets you test different users or sessions without state leaking between them:

const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  locale: 'en-US'
});

const firstPage = await context.newPage();
const secondPage = await context.newPage();

await firstPage.goto('https://example.com/one');
await secondPage.goto('https://example.com/two');

Context-level settings apply to its pages, including viewport, locale, and network routing. Create another context when you need independent cookies or cache. Close a directly created context before the browser so HAR files, videos, and other context artifacts can be flushed. The Browser API covers lifecycle details, and the Pages guide explains the page/context model.

Common problems and precise fixes

“Protocol error” or invalid URL

Cause: The string is malformed or lacks a resolvable scheme/base URL.
Fix: Pass an absolute URL such as https://example.com, or configure baseURL and use a valid path.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The call completes but the app is still rendering

Cause: The application populates data after the load event.
Fix: Wait for the specific heading, locator, URL, or application state your workflow requires. Use expect(...).toBeVisible() or a suitable locator assertion instead of a fixed sleep.

A 404 does not throw

Cause: HTTP error statuses are responses, not necessarily transport failures.
Fix: Store the response and check response.ok() or response.status().

A click-navigation wait intermittently times out

Cause: The URL wait was started after the click, or the action did not navigate at all.
Fix: Create the waitForURL promise before clicking, then verify that the control actually changes the URL or opens a popup.

Authentication or locale differs between runs

Cause: Cookies, cache, viewport, locale, or routes belong to a different context configuration.
Fix: Set those options when creating the context and use separate contexts for separate identities.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The browser closes before artifacts are complete

Cause: The browser was closed while its context still had files to flush.
Fix: Close the context first, then the browser.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance and reliability choices

  • Reuse one browser process when practical, but create a fresh context for each isolated user or test scenario.
  • Choose commit or domcontentloaded when the next operation does not need every load resource.
  • Use assertions tied to user-visible state for single-page applications and lazy-loaded content.
  • Check the main response status when availability or correctness depends on HTTP success.
  • Keep URL waits and action-triggered navigation paired so fast transitions are not missed.
  • Set viewport, locale, cookies, and routes at context creation so navigation is reproducible.

Or skip the browser setup

If your goal is a clean image or PDF rather than browser automation, ScreenshotNeo accepts one request with a URL. It handles consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.

Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every feature is included on every plan. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', data);

See the ScreenshotNeo documentation for request options. Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can two pages in one Playwright context navigate at the same time?

Yes. A context can contain multiple pages, and each page can call goto() independently. They still share that context’s cookies, cache, and emulation settings.

What should I log when a navigation failure is intermittent?

Log the requested URL, page.url() at failure, the navigation timeout, and any returned response status. This separates an HTTP error response from a transport or timeout failure.

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.