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.

Use a real Chrome session, not an HTTP client. In PHP, Symfony Panther gives you a WebDriver-based browser for navigation, waits and assertions, while chrome-php/chrome gives you direct control of Chrome or Chromium. Both execute the page’s JavaScript. Panther is usually the better fit for end-to-end tests and crawling; chrome-php/chrome is the more direct choice when your code needs to evaluate scripts, capture screenshots or generate PDFs.

Why an HTTP request cannot execute page JavaScript

file_get_contents(), cURL and Guzzle download the response body that the server sends. They do not create a browser document, run scripts, apply client-side routing or wait for fetch/XHR calls. If a site renders its useful content only after JavaScript runs, an HTTP-only scraper sees an empty shell or incomplete markup.

A headless browser runs Chrome without displaying a window. It uses the same browser engine as a visible session; Chrome for Developers describes headless mode as sharing code with Chrome (official documentation). Your PHP process controls that browser through WebDriver or Chrome’s debugging protocol, then reads the resulting DOM, clicks elements, waits for asynchronous content and saves artifacts.

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.

Choose the PHP control layer

Option Best fit What it provides Important setup
Symfony Panther Symfony tests, end-to-end checks and browser crawling WebDriver client, navigation, selector waits, screenshots, headless/visible modes and remote-browser support Chrome/Chromium plus a compatible ChromeDriver
chrome-php/chrome Direct PHP automation outside a testing framework Launch Chrome, open pages, evaluate JavaScript, take screenshots and create PDFs Chrome/Chromium and the Composer package

There is no directly comparable performance benchmark in the cited material, so choose by API and deployment needs rather than an assumed speed advantage. Panther’s documentation also names Selenium Grid, SauceLabs and BrowserStack as remote testing options; verify current service and browser compatibility before committing to one.

Route A: execute JavaScript with Symfony Panther

1. Install Panther and Composer autoloading

For a test-only dependency in a Composer project:

composer require --dev symfony/panther

In a standalone script, load Composer’s autoloader yourself. The package can be used outside a Symfony application.

<?php
require __DIR__ . '/vendor/autoload.php';

use SymfonyComponentPantherPantherTestCase;

Panther’s current documentation is the authority for exact package versions and APIs: symfony.com/doc/current/testing/end_to_end.html.

2. Start a headless Chrome client and wait for rendered content

The following example requests a page, waits for an element that appears after JavaScript rendering, extracts its text and writes a screenshot. Replace the URL and selector with values from the site you control or are authorized to access.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<?php
require __DIR__ . '/vendor/autoload.php';

use SymfonyComponentPantherPantherTestCase;

$client = PantherTestCase::createPantherClient([
    'browser' => PantherTestCase::CHROME,
]);

$client->request('GET', 'https://example.com/dashboard');

// Wait until the application has inserted the asynchronous result.
$client->waitFor('.results');

$heading = $client->getCrawler()->filter('.results h1')->text();
file_put_contents(__DIR__ . '/results.txt', trim($heading));
$client->takeScreenshot(__DIR__ . '/dashboard.png');

$client->quit();

In a Panther test class, the usual pattern is to extend PantherTestCase and call $this->createPantherClient(). The standalone form above illustrates the same browser workflow; consult the current Symfony page if your installed release exposes a slightly different factory signature.

3. Click controls and wait for the next state

Do not sleep for an arbitrary number of seconds when a selector expresses the state you need. Click the control, then wait for the result or for a loading element to disappear.

$client->getCrawler()->filter('button[data-action="load-more"]')->click();
$client->waitFor('.results li:nth-child(20)');

$items = $client->getCrawler()->filter('.results li');
foreach ($items as $item) {
    echo trim($item->textContent) . PHP_EOL;
}

Selectors must match the post-JavaScript DOM. A link that looks like a normal URL may instead be a button handled by an event listener; browser automation can trigger that interaction where an HTTP request cannot.

ChromeDriver, binaries and headless configuration

Install or expose ChromeDriver

Panther controls Chrome through WebDriver. Symfony documents the dbrekelmans/browser-driver-installer package and the command:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
vendor/bin/bdi detect drivers

You can alternatively place a compatible ChromeDriver in PATH or in the project’s drivers/ directory. The retrieved documentation does not establish a permanent browser/driver version pairing, so check current compatibility guidance whenever you pin Chrome, Chromium or a driver in CI.

Select the browser binary

If Chrome is not at the system default location, set PANTHER_CHROME_BINARY to the executable path in the process environment. This is useful for a packaged Chromium binary or a container image.

export PANTHER_CHROME_BINARY=/usr/bin/chromium

Run visibly while debugging

Headless mode is convenient for servers, but a visible browser makes selector and timing problems easier to diagnose. Set:

export PANTHER_NO_HEADLESS=1

Use PANTHER_CHROME_ARGUMENTS for additional Chrome flags supported by your environment. Panther also documents PANTHER_NO_SANDBOX; disabling Chrome’s sandbox is explicitly unsafe and should not be a routine optimization. Only consider it when you understand the isolation consequences of the container or host and have an appropriate security review.

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

CI and containers

  • Install Chrome or Chromium and make the binary path deterministic.
  • Install a matching ChromeDriver and verify it is executable by the CI user.
  • Keep tests headless unless you provide a display server for visible mode.
  • Capture browser and driver logs when a session fails to start.
  • Do not add --no-sandbox casually; treat it as a security exception, not a performance setting.

Route B: control Chrome directly with chrome-php/chrome

chrome-php/chrome is a Composer library for launching Chrome or Chromium, navigating, evaluating JavaScript, taking screenshots and creating PDFs. It is a natural choice when you want browser primitives without Panther’s WebDriver testing layer.

Install and run a page

composer require chrome-php/chrome
<?php
require __DIR__ . '/vendor/autoload.php';

use HeadlessChromiumBrowserFactory;

$factory = new BrowserFactory();
$browser = $factory->createBrowser([
    'headless' => true,
]);

try {
    $page = $browser->createPage();
    $page->navigate('https://example.com/app')->waitForNavigation();

    // Run JavaScript in the loaded page.
    $result = $page->evaluate('document.querySelector(".results")?.innerText || ""')->getReturnValue();
    file_put_contents(__DIR__ . '/results.txt', $result);

    $page->screenshot()->saveToFile(__DIR__ . '/app.png');
    $page->pdf()->saveToFile(__DIR__ . '/app.pdf');
} finally {
    $browser->close();
}

For a page that renders after an XHR, wait on the condition your application exposes rather than assuming navigation means that all data is ready. The library’s current README is the source for exact method names and options. At the time of the cited README, it listed PHP 7.4–8.5 and Chrome/Chromium 65 or newer, with Linux testing and macOS/Windows compatibility; these requirements can change, so check the repository before choosing production versions.

Waiting correctly for asynchronous JavaScript

Most “JavaScript did not run” reports are timing or selector problems. Navigation completion only says that the initial document loaded. A single-page app may still be fetching data, hydrating components or replacing placeholder nodes.

  • Wait for presence: use Panther’s selector wait for the element that proves rendering completed.
  • Wait for a state change: wait for a result row, a non-empty text node or the disappearance of a spinner.
  • Use a bounded timeout: fail with a useful diagnostic instead of hanging indefinitely.
  • Inspect the rendered DOM: save the page source or screenshot at failure time; the server HTML may not contain the final content.
  • Handle consent and authentication: provide authorized cookies, headers or a login flow before requesting protected content.

Performance, reliability and operating cost

A browser is heavier than an HTTP client because it starts a Chromium process, creates a renderer and downloads page resources. Reuse a browser process when your workload permits, close pages deterministically and avoid loading assets you do not need. Keep concurrency below the memory and CPU limits of the host, then measure your own workload; the cited sources provide no apples-to-apples benchmark.

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

For reliability, pin Composer dependencies, Chrome and the driver together in CI, but review compatibility when upgrading. Prefer selector-based waits over fixed sleeps, record the target URL and browser logs for failures, and retry only transient navigation errors. Do not retry a destructive click blindly: a second attempt could submit a form twice.

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

Troubleshooting

“ChromeDriver executable not found”

Cause: the driver is absent or not visible to the process. Fix: run vendor/bin/bdi detect drivers, place the driver in PATH or the project’s drivers/ directory, and confirm the CI user can execute it.

Session starts, then immediately exits

Cause: Chrome and ChromeDriver are incompatible, or the binary path is wrong. Fix: print the versions, set PANTHER_CHROME_BINARY to the actual executable and follow current compatibility guidance rather than relying on an old pairing.

The selector timeout expires

Cause: the selector is wrong, content is inside an iframe or the request failed. Fix: save a screenshot, inspect the rendered DOM, verify the frame and check browser/network logs. Wait for an application-specific result instead of increasing the timeout without evidence.

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

It works locally but fails in CI

Cause: missing display support, different filesystem paths, permissions, sandbox restrictions or a different browser binary. Fix: run headless, set absolute paths, install the browser and driver in the image, and preserve logs and screenshots as CI artifacts.

The page is blank or blocked

Cause: bot protection, authentication, geo restrictions, JavaScript errors or a failed third-party resource. Fix: confirm you are authorized to automate the site, provide the required session state, inspect console/network errors and treat CAPTCHA challenges as a boundary to respect rather than bypass.

Or skip the browser setup

If your goal is a reliable screenshot or PDF rather than maintaining Chrome and ChromeDriver, ScreenshotNeo provides a single HTTP request. It accepts 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/CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.

Install no browser locally for this call:

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 all options, including full-page lazy-image capture, CSS-selector element shots, device and viewport settings, retina scale, PDF paper and page ranges, custom CSS/JavaScript, clicks, waits, resource blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, async webhooks, bulk capture and the usage API.

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

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.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://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
require('fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account.

Frequently Asked Questions

Can Panther run without Symfony?

Yes. Symfony’s documentation describes standalone use; install the Composer package and require vendor/autoload.php in your PHP script.

Should I use Panther or chrome-php/chrome for PDF output?

Both routes can produce browser artifacts, but chrome-php/chrome exposes PDF creation directly. Choose Panther when WebDriver-based tests, selector waits and remote test infrastructure are the priority.

Is headless Chrome the same browser engine as normal Chrome?

Chrome for Developers states that headless mode shares code with Chrome. Differences in display availability and environment can still affect fonts, permissions and timing.

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

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.