ScreenshotNeo

BlogHow-to

How to Go Back to the Previous Page with Puppeteer

Use Puppeteer’s page.goBack() to return to the previous browser history entry. Learn how to handle missing history, wait options, timeouts, and SPA navigation.

By the ScreenshotNeo team4 October 20266 min read

Use await page.goBack() to navigate a Puppeteer page to the previous browser history entry. The method returns HTTPResponse | null: it can return null for same-page navigation, and it rejects if there is no previous history entry. Catch that rejection when the history state is uncertain. See the official Page.goBack() reference.

1. Minimal example

Call goBack() after an action that has created a history entry, such as navigating to another URL:

const response = await page.goBack();
console.log(response); // HTTPResponse or null

Keep the returned value if you need the main resource response. Do not assume it is always non-null. If the navigation involved redirects, Puppeteer resolves with the response from the last redirect.

2. Complete runnable example

This ES module example launches Puppeteer, navigates between two pages, goes back, handles a missing history entry, and closes the browser even if navigation fails. Install Puppeteer in your project with npm install puppeteer, then save this as back.mjs and run node back.mjs.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();

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

  try {
    const response = await page.goBack();
    if (response) {
      console.log('Returned to the previous entry:', response.url());
    } else {
      console.log('Navigation completed without a main resource response.');
    }
  } catch (error) {
    console.error('Could not go back in this page history:', error);
  }
} finally {
  await browser.close();
}

The history entry must exist by the time goBack() runs. In production code, put the call after the action that changes the page and handle a possible rejection if that action might not have produced an entry.

3. Waiting for navigation and timeouts

goBack() accepts optional WaitForOptions, so you can choose when Puppeteer considers the navigation ready. For example:

await page.goBack({ waitUntil: 'domcontentloaded' });

Use the readiness condition that matches the next operation. If you need the page’s load event, use 'load'; if the app continues making requests after document readiness, waiting for network idle may be more appropriate. Network-idle conditions can take longer or time out on pages with persistent requests.

The default navigation timeout is configurable in milliseconds with page.setDefaultNavigationTimeout(timeout). You can also set the timeout for this call through its wait options:

page.setDefaultNavigationTimeout(30_000);
await page.goBack({ waitUntil: 'domcontentloaded', timeout: 30_000 });

Choose a timeout based on the site’s expected response and your job deadline. A longer timeout does not fix a stalled page; it only allows more time before the call fails. See the official navigation timeout method reference.

4. History behavior, including single-page apps

“Previous page” means the previous entry in that page’s browser history. It is not necessarily the previous URL your code visited: redirects, links, form submissions, and client-side routing can affect the history sequence.

Puppeteer treats URL changes made through the History API as navigation too. A single-page application can therefore provide a history entry for goBack() without loading a new document. The app must actually have pushed or otherwise created an entry; changing UI state alone does not guarantee that Back has somewhere to go. For SPA details, see the Puppeteer FAQ.

A same-page navigation can resolve with null, as documented by the method reference. Distinguish that result from a thrown error: null is a fulfilled call without a main resource response, while a rejection means the navigation could not complete, including when there is no prior history entry.

5. Common errors and fixes

Symptom Likely cause What to do
goBack() rejects because there is no previous entry The page is at the start of its history, or the preceding action did not create a history entry. Ensure the navigation or app route change happened first. If history is uncertain, catch the rejection and choose a fallback in your application.
The result is null The navigation was same-page and did not provide a main resource response. Treat null as a valid return value; do not dereference it as an HTTPResponse.
The call times out The target page did not meet the chosen wait condition within the navigation timeout. Use an appropriate waitUntil condition, inspect whether the page keeps requests open, and adjust the timeout only when the expected load warrants it.
The page appears unchanged in an SPA The app may have changed local UI state without adding a browser history entry, or the relevant route change has not completed. Confirm the app uses the History API for the transition and wait for a route-specific condition after going back.
The returned response is unexpected after redirects The navigation followed a redirect chain. Puppeteer documents that the resolved response is from the last redirect. Inspect its URL and status rather than assuming it represents the first redirect.

6. Reliability, performance, and protocol notes

goBack() is a browser-history operation, not a direct request to a URL. Its outcome depends on the page’s current history and the site’s navigation behavior. Handle rejection for uncertain history, and handle the nullable response independently. Choose a wait condition that is sufficient for the next step; waiting for full page load or network idle can add avoidable latency when the task only needs document readiness.

Puppeteer lists Page.goBack() as fully supported with WebDriver BiDi. Chrome uses CDP by default unless BiDi is selected, while Firefox uses BiDi by default. Protocol behavior can depend on the Puppeteer version, so check the official BiDi compatibility page for the version and browser in your setup.

Cost depends on where and how you run the browser; the Puppeteer method itself does not define a per-navigation price. For reliable automation, keep navigation timeouts bounded, close browser instances in a finally block, and avoid retrying Back blindly because a retry can move to an additional history entry.

7. Or skip the browser setup

If your task is to capture a page image or PDF rather than control browser history, ScreenshotNeo provides a website screenshot API and MCP server. A single GET request can return PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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 are accepted like a visitor, and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
  • Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers report the page verdict and billing status.
  • An MCP server gives AI agents tools for screenshots, page information, and PDF capture.
  • The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

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

8. FAQ

Does page.goBack() return the previous URL?

It returns an HTTPResponse or null, not a URL string. When a response exists, call response.url() to read its URL.

Can I call it immediately after page.goto()?

Yes, if the page has a prior history entry. A first navigation in a fresh page may leave no previous entry, so handle rejection when that is possible.

Does Back work after a client-side route change?

It can when the app created a browser history entry, such as through the History API. A UI update without a history entry gives the browser nothing to return to.

Should I retry if it fails?

First identify whether the failure came from missing history or a navigation timeout. Retrying without checking state may go back farther than intended if the first operation actually changed history.