ScreenshotNeo

BlogHow-to

How to Fix Selenium Firefox Screenshots with Incorrect Height

Diagnose Firefox screenshot height mismatches by separating viewport, window, and full-page capture, then measure the PNG and reproduce your exact stack.

By the ScreenshotNeo team1 October 20267 min read

Direct answer: Selenium Firefox height problems usually come from comparing different things: the visible viewport, the browser window, and the full document. Use save_screenshot() for the current window, use Firefox’s full-page screenshot method for the entire document, and inspect the saved PNG’s actual pixel dimensions. A configured window size does not guarantee that the output image has the same dimensions.

Older reports show both kinds of failure. A 2020 geckodriver report described a configured and reported window of 1024×768 producing a 1024×694 PNG in headless Firefox. A separate 2019 Mozilla report described a full-page request returning viewport height. Those reports identify historical symptoms, not a universal current Firefox defect or a guaranteed workaround for every stack.

1. Decide which height you need

Goal Meaning Selenium approach
Visible browser area The current viewport or window only driver.save_screenshot("shot.png")
Entire page The document from top to bottom, including content below the fold driver.get_full_page_screenshot_as_file("full.png") or save_full_page_screenshot
Exact output pixels A known image width and height for a visual test Set the window, then measure the PNG; do not assume the request equals the artifact

Selenium’s Python Firefox API documents current-window screenshot methods separately from full-document methods. Choose the method that matches the image you intend to compare or publish: Selenium Firefox WebDriver API.

2. Reproduce and measure the mismatch

Start with a small page and record both Selenium’s reported window size and the PNG dimensions. Pillow reads the image header without changing the screenshot.

from pathlib import Path
from PIL import Image
from selenium import webdriver
from selenium.webdriver.firefox.options import Options

URL = "https://example.com"
OUTPUT = Path("firefox-shot.png")

options = Options()
options.add_argument("-headless")

driver = webdriver.Firefox(options=options)
try:
    driver.set_window_size(1024, 768)
    driver.get(URL)

    print("reported window:", driver.get_window_size())
    print("inner viewport:", driver.execute_script(
        "return {width: innerWidth, height: innerHeight}"
    ))
    print("document:", driver.execute_script(
        "return {width: document.documentElement.scrollWidth, "
        "height: document.documentElement.scrollHeight}"
    ))

    driver.save_screenshot(str(OUTPUT))
    with Image.open(OUTPUT) as image:
        print("PNG pixels:", image.size)
finally:
    driver.quit()

Compare the intended value with the right measurement:

  • For a viewport capture, compare the PNG with innerWidth and innerHeight after accounting for browser and platform behavior.
  • For a full-page capture, compare the PNG height with the document’s scrollable height, allowing for browser implementation details.
  • For a fixed visual-regression baseline, treat the measured PNG dimensions as the source of truth and fail the test when they change unexpectedly.

3. Use the correct full-page Firefox method

When the expected image includes content below the fold, call a full-document API rather than the current-window method.

from selenium import webdriver
from selenium.webdriver.firefox.options import Options

options = Options()
options.add_argument("-headless")
driver = webdriver.Firefox(options=options)
try:
    driver.set_window_size(1280, 900)
    driver.get("https://example.com")
    driver.get_full_page_screenshot_as_file("full-page.png")
finally:
    driver.quit()

Depending on your Selenium binding version, the API may also be exposed as save_full_page_screenshot, a binary-returning full-page method, or a base64-returning method. Check the API reference for the exact installed binding. If a full-page call returns only viewport height, record the complete environment and reproduce it before applying a version-specific workaround.

4. Record the environment before changing versions

Height behavior can depend on the operating system, headless mode, Firefox, geckodriver, Selenium, and the selected screenshot endpoint. Save this information with every failing artifact.

import platform
import selenium
from selenium import webdriver
from selenium.webdriver.firefox.options import Options

options = Options()
options.add_argument("-headless")
driver = webdriver.Firefox(options=options)
try:
    caps = driver.capabilities
    print({
        "os": platform.platform(),
        "python": platform.python_version(),
        "selenium": selenium.__version__,
        "browser": caps.get("browserVersion"),
        "geckodriver": caps.get("moz:geckodriverVersion"),
        "headless": True,
        "window": driver.get_window_size(),
    })
finally:
    driver.quit()

The 2020 report used Firefox 78.0.2, geckodriver 0.26.0, Selenium 3.141.0, Python 3.7, and macOS 10.15.4; it reported a 1024×768 window and a 1024×694 PNG. Mozilla’s usage documentation discusses Selenium/geckodriver compatibility, and its support documentation notes that geckodriver is not fully feature complete: Usage and Supported platforms.

5. A repeatable diagnostic checklist

  1. State whether you want viewport, current-window, or full-document output.
  2. Set the window size before navigation and wait until the page is ready.
  3. Print get_window_size(), innerWidth/innerHeight, and document scroll dimensions.
  4. Save the screenshot and inspect its actual PNG width and height.
  5. Repeat once in headed mode and once in headless mode if your deployment allows it.
  6. Record OS, Firefox, geckodriver, Selenium, Python, dimensions, and screenshot method.
  7. Reduce the page to a minimal reproduction before changing dependencies.

6. Common errors and fixes

Symptom Likely cause Fix
Image is shorter than the page Current-window capture was used Use the full-page Firefox method and verify the PNG dimensions.
Image is 1024×694 while the window says 1024×768 Window metrics and screenshot pixels differ in the reported headless stack Measure the artifact, record versions, and reproduce with your current stack; do not assume the 2020 report applies today.
Full-page request has viewport height Historical Firefox/geckodriver full-page behavior or a binding/driver mismatch Confirm that the full-page endpoint is actually called, then test a supported, current stack and keep a minimal reproduction.
Changing set_window_size has no effect Size was set after capture, overridden by a harness, or confused with CSS viewport size Set it before navigation, print both window and innerHeight, and inspect the PNG.
Headless and headed outputs differ Different rendering environments or browser chrome/platform behavior Pin the mode used in production and keep separate baselines when necessary.
Full page is clipped or content is missing Lazy loading, asynchronous layout, or a page that changes while captured Wait for a stable selector or application-ready signal, scroll or trigger lazy content when appropriate, then capture.
Screenshot call fails Driver/browser incompatibility or a crashed session Print capabilities, update or align Firefox and geckodriver, and rerun the minimal page.

7. Stabilize dynamic pages

Even with the correct height API, a page can change while the screenshot is being assembled. Wait for a deterministic condition instead of relying only on a fixed sleep.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

wait = WebDriverWait(driver, 30)
driver.get("https://example.com")
wait.until(lambda d: d.execute_script("return document.readyState") == "complete")
wait.until(lambda d: d.find_element(By.CSS_SELECTOR, "main"))
# Capture only after the layout is stable enough for your test.

For pages with lazy images, verify that the images you need have loaded. A full-document screenshot is not a substitute for waiting on application state.

8. Performance, reliability, and cost considerations

  • Viewport captures are cheaper to render: they contain fewer pixels and avoid stitching or full-document layout work.
  • Full-page captures can be large: long documents increase memory, encoding time, and artifact size. Split very long pages when your test objective allows it.
  • Pin the execution environment: browser and driver updates can alter layout and screenshot dimensions. Store the versions beside visual baselines.
  • Use deterministic waits: arbitrary sleeps make failures slower and still allow races.
  • Validate dimensions in CI: reject an unexpected height before running expensive image comparisons.
  • Keep failures observable: save the HTML URL, method, capabilities, requested dimensions, measured dimensions, and driver logs.

9. Or skip the browser setup

ScreenshotNeo provides a website screenshot API when you need a stable capture service instead of managing Firefox and geckodriver. It supports full-page capture with lazy images loaded, custom viewports and device presets, retina scale, element selectors, waits, custom JavaScript and CSS, and PDF output. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp
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)
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(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

See the ScreenshotNeo API documentation for options and response headers. Its MCP server includes take_screenshot, get_page_info, and capture_pdf tools for 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. Create a free ScreenshotNeo account.

10. FAQ

Does get_window_size() tell me the PNG size?

No. It reports browser window metrics. Always inspect the encoded image’s pixel dimensions.

Should I always use full-page screenshots?

No. Use a current-window screenshot for viewport tests and a full-page method only when content below the fold is part of the requirement.

Is the 1024×694 result a Firefox rule?

No. It was a 2020 issue report tied to a specific headless stack.

Can a full-page screenshot still miss content?

Yes. Dynamic layout, lazy loading, and content that changes during capture can produce incomplete output. Wait for application-specific readiness.

What is the fastest way to isolate a regression?

Capture a minimal page, print viewport and document metrics, save the PNG, and record every browser, driver, Selenium, OS, and headless setting.