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.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Set the timeout for the operation that can stall, not for “screenshots” as one universal task. In a Ruby browser script, navigation, JavaScript waits, remote-driver communication, element lookup and image capture have separate limits. With Ferrum, configure a page-level command timeout and, when needed, pass a shorter timeout to screenshot. With Selenium, set page_load for navigation and configure the Ruby HTTP client’s read timeout when a remote driver connection is the slow part.

The examples below keep navigation, readiness checks and capture as distinct steps so a slow page fails predictably instead of leaving a process hanging.

What a screenshot timeout actually controls

A screenshot workflow normally has four stages:

  1. Navigation: the browser requests the URL and follows redirects.
  2. Readiness: your application waits for a selector, a state change or a known delay.
  3. Capture preparation: the browser may resolve a CSS selector, calculate bounds or load lazy content.
  4. Image transfer: the driver returns PNG, JPEG or another encoding to Ruby and Ruby writes it to disk.

A page-load timeout bounds the first stage. It does not prove that a single-page application has finished rendering, and it does not necessarily bound the time needed to find an element or transfer the screenshot. Treat each stage as a separate failure point and rescue the exception appropriate to the library you use.

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

Ferrum: bound navigation and screenshot commands

Ferrum is “a high-level API to control Chrome in Ruby,” according to the Ferrum project documentation maintained by rubycdp. Its quick-start flow is a navigation followed by a screenshot:

#1 Best Overall
browser = Ferrum::Browser.new
browser.go_to("https://example.com")
browser.screenshot(path: "example.png")
browser.quit

Ferrum uses a page command timeout for browser operations. The default is the page timeout, and individual callers such as screenshot and PDF can receive a command-level override. Constructor option names and defaults can vary by the gem version pinned in your lockfile, so confirm them in that installed version’s API before copying an initializer into production.

A version-conscious Ferrum pattern

Keep the timeout value in one place, pass a capture-specific override when your version supports it, and always close the browser:

require "ferrum"

NAVIGATION_TIMEOUT = 30
CAPTURE_TIMEOUT = 15

browser = Ferrum::Browser.new(timeout: NAVIGATION_TIMEOUT)
begin
  browser.go_to("https://example.com")

  # Replace this with a condition that represents readiness for your page.
  browser.at_css("body")

  browser.screenshot(
    path: "example.png",
    timeout: CAPTURE_TIMEOUT,
    full: true,
    format: "png"
  )
rescue Ferrum::TimeoutError => e
  warn "Browser operation timed out: #{e.message}"
  exit 1
ensure
  browser.quit
end

If your installed Ferrum release does not accept timeout: on screenshot, remove that keyword and rely on the page-level timeout after checking the method signature for your version. Do not assume a timeout argument added in one release exists in another.

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

Wait for the page state you need

A successful navigation can still leave an application rendering data. Prefer a page-specific readiness condition over an arbitrary sleep:

browser.go_to("https://example.com/dashboard")
browser.at_css("[data-page-ready='true']", timeout: 20)
browser.screenshot(path: "dashboard.png", timeout: 15)

For a selector capture, Ferrum must resolve the element and calculate its bounds before encoding the image. That lookup is another operation that can consume the page timeout. Use a stable selector and make the selector timeout long enough for the application to render, but short enough to expose a broken page.

Useful Ferrum capture options

Ferrum’s screenshot API supports viewport and full-page capture, selector or area capture, output path and encoding, image format, quality, scale and background settings. A full-page shot can trigger lazy-image loading; a selector shot adds element-bound calculation. For example:

browser.screenshot(
  path: "hero.webp",
  format: "webp",
  quality: 85,
  scale: 2,
  selector: ".hero",
  background: true
)

Use only options supported by your installed gem version. If a capture times out, first test a viewport screenshot without a selector or full-page mode. That isolates page rendering from bounds calculation and lazy-content work.

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

Selenium Ruby: separate page-load and transport timeouts

Selenium exposes a page-load timeout in seconds. This documented pattern limits navigation, then saves the current browser view:

require "selenium-webdriver"

driver = Selenium::WebDriver.for :chrome
begin
  driver.manage.timeouts.page_load = 30
  driver.navigate.to("https://example.com")
  driver.save_screenshot("example.png")
ensure
  driver.quit
end

page_load is a navigation bound, not a universal deadline for the screenshot operation. Selenium also exposes a separate asynchronous-script timeout for calls such as execute_async_script:

driver.manage.timeouts.script = 10
driver.execute_async_script(<<~JS)
  const done = arguments[arguments.length - 1];
  Promise.resolve(window.appReady === true).then(done);
JS

For a remote driver, the Ruby binding has a distinct HTTP-client read timeout. Configure that client before creating the driver, using the API exposed by the Selenium version in your bundle. This protects the Ruby-to-driver transport when the browser is reachable but a command response is delayed; it does not replace page_load or an application readiness condition.

A Selenium readiness and capture sequence

require "selenium-webdriver"

driver = Selenium::WebDriver.for :chrome
begin
  driver.manage.timeouts.page_load = 30
  driver.manage.timeouts.script = 10

  driver.navigate.to("https://example.com/app")
  wait = Selenium::WebDriver::Wait.new(timeout: 20)
  wait.until { driver.find_element(css: "[data-page-ready='true']").displayed? }

  driver.save_screenshot("app.png")
rescue Selenium::WebDriver::Error::TimeOutError => e
  warn "Selenium operation timed out: #{e.message}"
  exit 1
ensure
  driver.quit
end

The explicit wait checks an application condition after navigation. If the selector never appears, the wait fails independently of the page-load limit, making the cause easier to diagnose.

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

Choosing values without creating false guarantees

There is no documented universal timeout that fits every site, driver and network. Choose values from the slowest legitimate case your service must support, then leave headroom for capture and cleanup.

  • Navigation: include DNS, TLS, redirects and server response time.
  • Readiness: include the client-side request and rendering needed for the specific screenshot.
  • Capture: allow extra time for full-page layout, lazy images, selector lookup and image encoding.
  • Transport: for a remote driver, allow the command response to cross the network without masking a dead session.

Keep the limits finite and log which stage failed. A single large timeout hides whether the site, browser, driver or output path is responsible.

Common timeout failures and fixes

Navigation times out, but the page works in a normal browser

Check redirects, authentication, geolocation and bot checks. Confirm that the URL is reachable from the machine running Chrome, then raise only the navigation limit if the extra delay is expected. If the page requires a post-load API call, keep navigation bounded and add a readiness wait instead of making navigation unlimited.

The selector wait or selector screenshot times out

Verify the selector in DevTools and check whether the element is inside an iframe or shadow DOM. Capture the viewport without selector to determine whether the page rendered. For dynamic lists, wait for a stable marker rather than the first transient element.

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

Full-page capture is slow or incomplete

Full-page mode can force layout work and lazy-image loading. Try a normal viewport capture, then disable nonessential animations with page CSS, or wait for images to finish loading. If the page is extremely long, capture a bounded element or split the job into sections.

Selenium raises an HTTP or connection timeout

This usually concerns communication with a remote driver, not page loading. Check the driver service, network route and the Ruby HTTP client's read-timeout setting. A larger page-load timeout will not repair a transport that cannot return a command response.

The screenshot file is missing or corrupt

Ensure the destination directory exists and the Ruby process can write to it. Use an absolute path while debugging. If the driver reports success but the file is empty, check disk space and capture a simple viewport PNG before testing WebP, high scale or full-page options.

The browser remains running after an error

Put quit in an ensure block. Leaked Chrome processes consume memory and can make later jobs appear to time out even when the website is healthy.

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

Reliability and performance practices

  • Pin Ferrum, Selenium, Chrome and the driver versions in deployment; verify timeout method signatures after upgrades.
  • Record URL, stage, elapsed time, exception class and browser/driver version for every failure.
  • Use a bounded retry only for transient network or driver errors. Repeating a deterministic selector failure wastes capacity.
  • Prefer one browser session per controlled batch, but restart after crashes or memory growth.
  • Disable unnecessary resources only when the screenshot does not depend on them; blocking fonts, scripts or images can change layout.
  • For parallel jobs, cap concurrency to the CPU and memory available for Chrome. More workers can increase, rather than reduce, capture latency.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One request returns a PNG, JPEG, WebP or PDF, so your Ruby service does not need to manage Chrome, Ferrum or Selenium:

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

See the ScreenshotNeo API documentation for request options and response details. The service can accept consent banners before capture and remove more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and whether the shot was billed. An MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create an account at ScreenshotNeo's free sign-up page.

Ruby, Python and Node.js calls to the same API

If a non-Ruby service owns the job, these equivalent requests use the same endpoint:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
# Ruby
require "requests" # Use your preferred HTTP client in production

# 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}`);

In Ruby, use a maintained HTTP client such as Net::HTTP or Faraday to issue the GET and write the response body in binary mode. Set the client timeout to cover the API request, and inspect the returned X-Page-Verdict and X-Billed headers when deciding whether to retry.

Ferrum or Selenium: which timeout model fits?

Need Ferrum Selenium Ruby
Browser stack Direct Ruby API for Chrome WebDriver-controlled browser and driver service
Navigation bound Page command timeout manage.timeouts.page_load
JavaScript wait Use a page-specific condition supported by your version manage.timeouts.script plus an explicit wait
Remote transport Depends on the Ferrum/Chrome connection configuration Ruby HTTP-client read timeout is separate
Capture modes Viewport, full page, selector and area options save_screenshot of the current browser view; other modes depend on driver/browser support

Choose the library already integrated with your project, then bind the operation that is actually stalling. Timeout numbers are not interchangeable between these layers.

Frequently Asked Questions

Does setting Selenium's page-load timeout stop a screenshot that hangs?

No. It bounds navigation. Add an explicit readiness wait and investigate the driver or HTTP-client timeout if the capture command itself is delayed.

Should I use a fixed sleep before every Ruby screenshot?

Usually not. Wait for a page-specific selector or state so fast pages finish quickly and slow pages fail for a meaningful reason.

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

Why can a Ferrum selector screenshot time out when a viewport screenshot succeeds?

Selector mode must find the element and calculate its bounds. A missing, hidden or late-rendered element can fail even though the document itself is available.

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.