ScreenshotNeo

BlogHow-to

Fix PHP Website Screenshots of Indian Portals That Time Out on Slow Connections

Make PHP screenshot jobs wait for the content they need, handle navigation timeouts deliberately, and diagnose slow Indian portals without assuming the server is down.

By the ScreenshotNeo team4 October 202610 min read

A PHP screenshot timeout means the browser operation exceeded its configured wait; it does not, by itself, prove the portal is down or identify whether the delay came from the server, network, browser, or a resource on the page. The practical fix is to choose an appropriate navigation milestone, then wait for the specific content your screenshot needs. Avoid waiting for every network connection to stop when the page has long-running requests.

This example uses Spatie Browsershot v4, a PHP wrapper around a browser automation process. The package and browser layer matter: PHP does not itself render a website screenshot, and another PHP library may have different methods or timeout defaults. Playwright’s navigation documentation describes the same general distinction between navigation milestones and page readiness, and discourages networkidle as a testing readiness condition. Playwright Page API

1. Identify which wait is timing out

Before changing a timeout, record the target URL, PHP package and version, browser version, configured timeout, full exception text, response status if available, and any browser console or failed-request messages. Reproduce the capture from the same machine or container that runs PHP. A page that loads on a developer’s laptop may behave differently from a queue worker with different DNS, proxy, firewall, TLS, or outbound network access.

Use this decision sequence:

  1. If the top-level document never responds, investigate reachability, DNS, TLS, redirects, server response time, and the capture environment.
  2. If the document responds but the browser times out waiting for load, determine whether late images, scripts, or other resources are actually required in the image.
  3. If the document loads but the screenshot misses content, wait for a page-specific signal such as the results panel or heading becoming visible.
  4. If that selector never appears, inspect the page state and failed requests. Extending the timeout alone cannot make a missing element appear.

Playwright documents commit, domcontentloaded, load, and networkidle as different navigation milestones. A timeout says the configured operation did not finish in time; it does not identify the underlying cause. Playwright Page API

2. Runnable PHP example with Browsershot

Install Browsershot and its required Node.js, Puppeteer, and Chromium dependencies according to the package’s current installation instructions. Make sure the PHP worker can execute the configured Node and browser binaries, and that the runtime user can write to the output directory.

composer require spatie/browsershot

Save the following as capture.php. Change the URL and readiness selector to match the portal. This example begins navigation at domcontentloaded, allows the navigation operation a deliberate 90-second budget, then waits separately for the page content needed in the screenshot. Adjust the budget to your own service’s limits and observed portal behavior; it is not a universal recommended timeout.

<?php

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

use Spatie\Browsershot\Browsershot;

$url = 'https://example.gov.in/';
$output = __DIR__ . '/portal.png';
$readySelector = 'main h1'; // Replace with a stable element on the target page.

try {
    Browsershot::url($url)
        // Do not wait for every late resource before beginning the readiness check.
        ->setOption('waitUntil', 'domcontentloaded')
        // The package/browser operation gets a bounded time budget in milliseconds.
        ->timeout(90000)
        // Wait for the actual content required by this screenshot.
        ->waitForSelector($readySelector, 30000)
        ->windowSize(1440, 1000)
        ->save($output);

    fwrite(STDOUT, "Saved screenshot: {$output}\n");
} catch (Throwable $error) {
    // Keep the original exception in logs; it contains the useful browser error.
    fwrite(STDERR, "Screenshot failed for {$url}: {$error->getMessage()}\n");
    exit(1);
}

Check the installed Browsershot version’s method signatures before copying into a long-lived application. Browsershot’s v4 documentation covers image creation and timeout configuration; its timeout controls the package’s browser operation, while the page’s readiness condition is a separate concern. Browsershot: creating images · Browsershot: setting the timeout

3. Choose the navigation milestone and readiness condition

Wait target What it means Use it when Watch out for
commit The response is received and document loading has begun. You want the earliest point to start checking page state. Most document content may not yet exist.
domcontentloaded The initial HTML document has been parsed. The needed content is in the document or appears shortly afterward. Client-side rendering may still be in progress.
load The document’s load event has fired. The screenshot depends on resources that block that event. A slow or failing resource can delay the event even when the main content is usable.
networkidle Network activity has been quiet for a defined period in the browser tool. Only when the target page’s traffic pattern makes this meaningful. Polling, analytics, streaming, or other continuing requests may prevent idle. Playwright explicitly discourages this for testing.
Selector or application signal A chosen element appears, becomes visible, or an app-defined ready condition becomes true. The screenshot needs a known result, heading, table, or page section. A brittle selector or failed application request can cause this wait to time out.

The Browsershot example uses waitUntil as a Puppeteer navigation option and then waitForSelector. Confirm the option mapping in your installed package version. The milestone names and semantics above are documented by Playwright; do not assume a different PHP wrapper exposes identical names or defaults. Playwright Page API

Prefer a stable selector tied to the content, for example a results container or page heading. Avoid selectors that only match a decorative element, a transient spinner, or a generic wrapper that exists before useful content arrives. If the portal provides an application-specific ready marker, use it. Keep a separate bounded timeout on the selector wait so a missing element fails with a useful error instead of hanging indefinitely.

4. Tune timeouts without hiding failures

There is no single timeout suitable for every Indian portal, network, browser host, or capture workload. Use measured timings from the actual capture environment. Set a navigation budget that accommodates the slow cases you intend to support, and a separate readiness budget for the page content. Keep the overall job deadline greater than the sum of its bounded stages, with room for browser startup and saving the image.

  • Do not set all waits to unlimited. An unreachable host or missing selector can then occupy a worker indefinitely.
  • Do not repeatedly retry every timeout. Retries multiply load and job time. Retry only transient failures, cap attempts, and use backoff.
  • Do not treat HTTP status and screenshot success as the same thing. A browser may render an error page or partial page; inspect the response and visible content for the task.
  • Keep the original exception and diagnostics. Log URL, elapsed time, browser/package versions, stage, response status, and request failures. Avoid logging credentials, session cookies, or private page content.

A timeout increase is appropriate when evidence shows the page is progressing and the desired content arrives just beyond the previous budget. It is not a fix for a redirect loop, unreachable host, blocked browser process, TLS error, or selector that never matches.

5. Capture diagnostics and handle partial failures

  1. Run a single capture with the exact URL and environment where the failure occurs.
  2. Record elapsed time and the last stage reached: browser launch, navigation, selector readiness, screenshot rendering, or file write.
  3. Inspect the top-level response and browser request failures where your wrapper exposes them. A main-document failure differs from a failed optional image.
  4. Save a trace, console output, or equivalent browser diagnostics if your automation stack supports it. Compare a successful and failed run.
  5. Verify that the output file exists, is non-empty, and belongs to the current run. Write to a temporary path and rename on success if downstream jobs must never read a partial artifact.
  6. Close browser resources and release queue locks in a finally block in long-running workers; avoid leaving stale Chromium processes after exceptions.

For capture systems that cannot expose browser internals, retain the exact error response and timing data your wrapper does provide. A timeout alone cannot say whether the portal, network, or local browser process caused the delay.

6. Common errors and fixes

Symptom Likely explanation What to do
Navigation timeout at the old default The selected milestone took longer than the configured budget. Choose an earlier milestone if later resources are unnecessary; wait for the needed selector; raise the bounded budget only when measurements show genuine progress.
Network idle never arrives The portal keeps requests open or continues background traffic. Use a page-specific readiness condition. Playwright discourages networkidle as a testing readiness check. Source
Selector wait timeout The selector is wrong, the content is not rendered, or an application request failed. Inspect the rendered DOM and failed requests; choose a stable selector for the actual result; do not merely extend the selector timeout.
Connection refused, DNS, or TLS error The browser host cannot reach the URL or cannot establish a trusted connection. Check DNS and outbound access from the worker/container, proxy configuration, certificate chain, and redirects. Do not disable TLS validation as a routine workaround.
Browser binary or Node process error Runtime dependencies are missing, paths differ for the PHP worker, or the worker user lacks permissions. Install/configure dependencies as documented for the package; check executable paths, environment, process limits, and writable output directories.
Screenshot is blank or incomplete Capture ran before client rendering or an application state transition finished. Wait for the content element or application-ready condition; verify that the page did not return a bot check, error page, or blank response.
Works locally but fails in production Different network access, DNS, proxy, TLS trust, CPU/memory, browser version, or worker limits. Reproduce from the production runtime and compare versions and connectivity. A longer timeout does not resolve environment differences.
Timeout increase causes queue pileups Slow jobs occupy workers longer, potentially while retries add duplicate work. Set job-level deadlines and bounded retries; measure concurrency and per-job duration; route consistently slow targets separately if needed.

7. If you maintain the portal, improve the slow path

Screenshot timing can reveal a real page performance problem, but it does not establish one by itself. If you own the site, measure the response and page behavior under slower network conditions, then inspect transfer size, server response time, redirects, render-blocking resources, and client-side requests.

The Guidelines for Indian Government Websites recommend keeping total file size to a minimum for acceptable download times, especially for users without high-speed, reliable internet; they also recommend testing across connection speeds and keeping content readable when stylesheets load slowly or fail. These are design recommendations, not evidence about the cause of a particular capture timeout. GIGW guidelines · GIGW scope

GIGW applies to government websites and applications at central, state, and local levels. The title may also describe non-government Indian sites, so apply the guidance as relevant rather than assuming every target is a government portal. GIGW scope and objective

8. Performance, reliability, and cost

Waiting for load or network idle can increase capture duration when optional resources are slow. Waiting only for a selector can reduce wasted waiting, but may produce an incomplete image if important fonts, images, or charts have not rendered yet. Choose a readiness condition that matches what the screenshot must prove or display.

Longer per-job waits reduce false timeouts at the cost of worker occupancy and queue throughput. Bounded retries help with transient network failures but increase latency and request volume. Track success rate by failure stage, elapsed time, and target group; those measurements help distinguish a timeout policy problem from a reachability or page-readiness issue.

Self-hosted capture has infrastructure and maintenance costs: browser installation, upgrades, memory and CPU capacity, process supervision, and debugging. A hosted screenshot API shifts browser operation out of the PHP app but adds a per-plan usage limit and a network dependency. Compare the time required to maintain your own browser stack with the cost and controls you need.

9. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request can return a PNG, JPEG, WebP, or PDF. For a quick PHP integration, use cURL from PHP’s command line or a worker. See the ScreenshotNeo API documentation for request options.

<?php

$url = 'https://example.gov.in/';
$apiKey = getenv('SCREENSHOTNEO_API_KEY');

if (!$apiKey) {
    throw new RuntimeException('Set SCREENSHOTNEO_API_KEY first.');
}

$query = http_build_query([
    'access_key' => $apiKey,
    'url' => $url,
]);

$ch = curl_init('https://api.screenshotneo.com/v1/shot?' . $query);
$output = __DIR__ . '/portal.webp';
$file = fopen($output, 'wb');

curl_setopt_array($ch, [
    CURLOPT_FILE => $file,
    CURLOPT_TIMEOUT => 90,
    CURLOPT_CONNECTTIMEOUT => 15,
]);

$ok = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$error = curl_error($ch);
curl_close($ch);
fclose($file);

if ($ok === false || $status < 200 || $status >= 300) {
    @unlink($output);
    throw new RuntimeException("Screenshot request failed (HTTP {$status}): {$error}");
}

The equivalent command-line request is:

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

Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.gov.in"},
    timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
    f.write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.gov.in' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: HTTP ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; each cleanup 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. Its MCP server gives AI agents tools to take screenshots, inspect page information, and capture PDFs. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. See the API options and documentation, then sign up for 1,000 free screenshots a month with no card.

10. FAQ

Does a navigation timeout mean the Indian portal is down?

No. It means the browser operation exceeded its configured time. Check response status, navigation errors, and resource failures from the capture environment before drawing a conclusion.

Should I always wait for networkidle?

No. Pages with continuing background requests may never become idle, and Playwright explicitly discourages this as a testing readiness signal. Wait for the content your screenshot needs.

Can PHP take a website screenshot by itself?

PHP typically coordinates a browser automation package or calls a screenshot service. The browser layer performs page rendering and capture; package APIs and dependencies vary.

What details are needed to find the exact cause?

The portal URL, capture package and version, exact error text, configured timeouts, and browser trace or network log. Without those, a cause-specific diagnosis would be guesswork.