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.

When PhantomJS works in a terminal but fails from PHP, the first things to compare are the executable path, the service account, the process environment, and the child process’s exit code and error output. Run the same PhantomJS script as the PHP/web-server user, then capture stdout, stderr, and the return code in PHP. If PhantomJS starts successfully, investigate page loading and rendering separately: log page.open status, JavaScript errors, network requests, and the destination file.

There is no single fix without the command, operating system, PhantomJS version, error, and target page. The steps below narrow the cause from process launch to page output. PhantomJS is also legacy software: its repository was archived in 2023 and the project wiki marks the 2.x branch deprecated, so production users should include a maintained-renderer migration plan.

Start with a controlled reproduction

Do not begin by changing PHP settings or installing Xvfb. First establish whether the problem is launching PhantomJS, loading the page, executing page JavaScript, or saving the result. Those stages fail differently and call for different fixes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. Find the exact binary. In a terminal, run command -v phantomjs (or the equivalent for your operating system), then run that absolute path with --version. Record both. PhantomJS’s troubleshooting guide warns that multiple installations can make a different version run than expected: PhantomJS troubleshooting.
  2. Run the script with that absolute path. Use the same script file and target URL as the PHP application. Record the command, exit status, stdout, stderr, and whether the expected output file appears.
  3. Repeat as the PHP service identity. Use the same account and service/container environment as the PHP worker or web server. Compare its PATH, working directory, environment variables, access to the executable and script, shared libraries, and output directory with the interactive shell. These comparisons are diagnostic checks; a successful interactive run does not establish that PHP has the same environment.
  4. Only then involve PHP. Use PHP to run the same absolute binary and script while capturing its return code and output. If direct execution as the service user also fails, fix installation, runtime, permissions, or host security before debugging the PHP call.

The official PhantomJS quick start treats PhantomJS as a command-line tool and shows rendering after a successful page-open callback. Its PHP exec() reference documents how to collect command output and a return value. Your application may use another process API, so apply the equivalent output and status capture to that API.

Capture what PHP actually ran

A frequent source of confusion is treating an empty result from PHP as proof that PhantomJS did nothing. The child process may have failed before launch, written diagnostics to stderr, returned a nonzero status, or run successfully while the PHP code discarded its output. Log the escaped command (without credentials or other secrets), the return code, stdout, and stderr. Also log the working directory and the exact output path.

For a minimal diagnostic using exec(), keep the binary path, script path, and URL fixed while you compare runs. Replace the paths and URL with your own values:

<?php
$binary = '/usr/local/bin/phantomjs';
$script = '/var/www/app/render.js';
$url = 'https://example.com/';

$command = escapeshellarg($binary) . ' ' .
            escapeshellarg($script) . ' ' .
            escapeshellarg($url) . ' 2>&1';
$output = [];
$returnCode = 0;
exec($command, $output, $returnCode);

error_log('PhantomJS command: ' . $command);
error_log('PhantomJS exit code: ' . $returnCode);
error_log('PhantomJS output: ' . implode("n", $output));
?>

For this diagnostic, 2>&1 sends stderr into the captured output so errors are not lost. If your process library captures stderr separately, keep it separate. Do not put API keys, authenticated URLs, cookies, or authorization headers into logs. Escaping each argument matters: concatenating an untrusted URL or path directly into a shell command can alter what the shell executes. Consult the documentation for the process API you actually use and preserve the exit code rather than assuming a PHP call’s return value is the child’s success indicator.

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

Make the PhantomJS script report page status

A successful process launch does not mean the page loaded or rendered. Have the script report the page.open callback status, render only on success, and exit on both success and failure. PhantomJS will not terminate unless the script tells it to exit; an unfinished asynchronous branch can leave PHP waiting.

var page = require('webpage').create();
var system = require('system');
var url = system.args[1];
var output = system.args[2] || '/tmp/render.png';

page.onError = function (message, trace) {
  console.error('PAGE ERROR: ' + message);
  trace.forEach(function (frame) {
    console.error('  at ' + frame.file + ':' + frame.line);
  });
};

page.onConsoleMessage = function (message) {
  console.log('PAGE CONSOLE: ' + message);
};

page.onResourceRequested = function (request) {
  console.log('REQUEST: ' + request.url);
};

page.open(url, function (status) {
  console.log('OPEN STATUS: ' + status);
  if (status !== 'success') {
    phantom.exit(2);
    return;
  }

  page.render(output);
  console.log('RENDERED: ' + output);
  phantom.exit(0);
});

Pass the output filename as the second script argument if you change the default. The callbacks make the stages visible: a non-success open status points to loading, while a page error or missing resource may explain absent content after launch. Page console messages are not forwarded by default; page.onConsoleMessage exposes them when you need the page’s own logs. The quick-start guide documents the open/render/exit pattern: PhantomJS Quick Start.

Branch on the symptom

“PhantomJS works in terminal but not in PHP”

Compare the executable and environment, not just the command text. Use an absolute binary path; check whether the PHP service user can traverse the executable’s parent directories and read the script and required libraries. Confirm the PHP worker’s PATH, working directory, environment, and permissions on the output directory. A shell profile may set values that a service process never receives. If the PHP process is in a container or restricted service, test inside that same environment rather than on the host.

“PhantomJS not working when called from PHP” or “command not found”

Check the captured command, exit status, and stderr. An absolute path avoids relying on PHP’s PATH. Confirm that the script path and arguments are quoted as individual arguments and that the application’s process-execution function is enabled and behaving as expected in that deployment. The title does not establish whether your code uses exec(), shell_exec(), or a process library; inspect the actual call rather than applying a fix for the wrong API.

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

“PhantomJS permission denied from PHP”

Read the exact stderr message, then test as the service account. Check execute permission on the binary, directory traversal permission along its path, read permission for the script and libraries, and write permission for the destination directory. If SELinux is enabled, check its policy and audit logs: the PhantomJS troubleshooting guide specifically notes that SELinux can prevent PhantomJS from working. Do not broadly disable host security controls as a diagnostic shortcut; identify the denied access and adjust policy narrowly where appropriate.

“PhantomJS cannot connect to X server”

Check the PhantomJS version before installing a display server. The official FAQ says versions 1.4 and earlier required an X server; starting with 1.5, PhantomJS was pure headless and did not require X11/Xvfb. Installing Xvfb solely because an old post recommends it may not address the actual failure on a newer version. See the PhantomJS FAQ.

HTTP loads but HTTPS does not

Investigate the SSL libraries available to the PhantomJS process, particularly OpenSSL, and capture its loading errors. The PhantomJS troubleshooting page identifies SSL libraries as a check when HTTPS fails. Verify within the same account/container that runs the binary; a library available to an interactive shell may not be available in a restricted runtime.

Windows proxy delay or unreachable resources

The project troubleshooting page describes a Windows default-proxy latency issue and gives --proxy-type=none as a workaround for that situation. Use it only when the symptoms fit and the process does not need a proxy; disabling proxy use can break access in environments where the proxy is required. For other missing assets, log requested resources and check the target page’s network requirements rather than applying the proxy switch blindly.

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

“PHP exec PhantomJS returns blank image”

First distinguish a blank page from a missing or invalid file. Check the child exit code, OPEN STATUS, page errors, console messages, and resource requests. If the page opened but expected content is absent, investigate page-specific JavaScript and network loading. If the file does not exist or cannot be read, verify the exact destination path and service-user write permissions. If the image is valid but transparent, inspect the page’s CSS: PhantomJS documents transparent output as expected when the page does not set a background color.

The render file has an unexpected format or contents

page.render(filename) saves an image buffer, and the filename extension selects the format. The render API lists PDF, PNG, JPEG, BMP, and PPM; GIF support depends on the Qt build. Check that the chosen extension and intended format agree, and verify the path actually passed to page.render. See the PhantomJS render API.

The PHP request hangs

Inspect every asynchronous path in the script. Ensure that each success and failure branch eventually calls phantom.exit(), after any work that must finish. The quick start explicitly warns that PhantomJS will not terminate without an exit call. If your script intentionally waits for page activity, make the wait condition and completion path observable rather than relying on an unbounded wait.

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

Use the output as a diagnostic, not just an artifact

A reliable investigation keeps evidence for each stage. For each test, record the binary path and version, service identity, command arguments, exit status, stdout/stderr, page-open status, page-side errors, resource requests, and output file path. Change one relevant condition at a time. For example, if the same account can run the binary but PHP cannot, focus on the process call and PHP environment; if both launch but only HTTPS fails, focus on SSL/runtime access; if the file exists but is transparent, focus on page background styling.

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

Do not infer a general performance limit from a sample load time or one page. Rendering duration can depend on the page, its resources, and the environment; this evidence base does not establish a benchmark. For operational reliability, treat timeouts, incomplete resources, missing exit paths, and write failures as distinct conditions and retain the process and page-level logs needed to identify them.

Plan for PhantomJS’s maintenance status

The PhantomJS GitHub repository was archived on May 30, 2023: PhantomJS repository. The project wiki labels the 2.x branch deprecated and no longer maintained: PhantomJS project wiki. That status does not prove migration will fix this particular PHP failure; it does mean a production rendering system should assess whether a maintained browser-rendering path is needed.

Evaluate a replacement against the constraints that matter in your deployment:

  • Can PHP launch it under the actual service identity and container/security policy?
  • Does its browser and JavaScript behavior fit the pages you must render?
  • What operating-system, headless-display, and runtime dependencies will deployment require?
  • Does it produce the image or PDF formats and fidelity your application needs?
  • What is the cost of changing invocation code, operational monitoring, and output validation?

Test representative pages and failure cases before switching a production workflow. The available evidence does not benchmark replacement products or establish a universal successor; choose based on the pages and deployment you actually support.

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

Or skip the browser setup

If your goal is to get a website screenshot rather than maintain a PhantomJS installation, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. One GET request returns an image or PDF, and its documentation covers the request options: ScreenshotNeo API docs.

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

Cookie banners, newsletter popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. An MCP server provides screenshot tools for AI agents, including Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month without a card; paid plans start at $5 for 3,000 shots. Sign up for 1,000 free screenshots a month with no card.

Frequently Asked Questions

Does PhantomJS 2.x require Xvfb?

No. The PhantomJS FAQ says versions 1.5 and later are pure headless; its X-server requirement applied to 1.4 and earlier.

Which PHP function should I use to run PhantomJS?

The diagnosis depends on your application’s existing process API. Capture the child process’s exit code and stdout/stderr using that API; the example above uses PHP’s documented exec() function.

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

Does a successful PhantomJS exit mean the page rendered correctly?

No. Check the script’s page-open status and page-side errors as well as the process exit status and output file.

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.