How to Use Browserless Screenshots in a WordPress Site Hosted in India
Call Browserless from WordPress with PHP, store and embed screenshot images, and understand hosting region, data handling, and common capture issues.
A WordPress site hosted in India can request a screenshot from Browserless with a server-side HTTPS POST. Use WordPress’s HTTP API to send the target URL and capture options to Browserless’s /screenshot endpoint, then handle the response as image bytes: save it to the Media Library or another controlled location and embed the resulting image URL. Keep the Browserless token on the server. Do not put it in page HTML or browser-side JavaScript.
The WordPress server’s location does not establish where Browserless runs the browser or stores related data. Check the selected Browserless endpoint’s execution region and its data-handling details before using the shared cloud service for sensitive pages.
1. What the integration does
The request flow is:
- WordPress code chooses a trusted target URL and capture options.
- PHP makes an HTTPS POST through the WordPress HTTP API, authenticating with the Browserless token.
- Browserless returns binary screenshot data, such as PNG, JPEG, or WebP.
- Your integration validates the response, saves or caches the image, and renders a safe image URL in the page.
Browserless accepts either a url or inline html as the capture source, along with Puppeteer-style screenshot options. Do not send both url and html in the same request. See the Browserless Screenshot API and the WordPress HTTP API guide.
2. Configure the token and add a small plugin
Create a site-specific plugin instead of editing a parent theme, so the integration is not lost during a theme update. Define the token in server-side configuration, for example in wp-config.php or an environment-backed secret that your deployment exposes to PHP:
define( 'BROWSERLESS_TOKEN', getenv( 'BROWSERLESS_TOKEN' ) );
Ensure the value is present in the PHP process and is not exposed through a publicly readable configuration file. Browserless’s shared REST examples use a token in the request URL; avoid logging full request URLs because they may contain credentials.
The following minimal plugin registers a shortcode. It intentionally only captures a URL supplied by trusted editorial configuration in the shortcode and applies basic URL checks. For a production site, prefer deriving the URL from a WordPress object or an allowlist, and restrict who can edit content containing this shortcode.
<?php
/**
* Plugin Name: Site Browserless Screenshot
* Description: Capture and display a Browserless screenshot with a shortcode.
*/
add_shortcode( 'browserless_screenshot', 'site_browserless_screenshot_shortcode' );
function site_browserless_screenshot_shortcode( $atts ) {
$atts = shortcode_atts(
array(
'url' => '',
'alt' => 'Website screenshot',
'full_page' => '0',
),
$atts,
'browserless_screenshot'
);
if ( ! defined( 'BROWSERLESS_TOKEN' ) || ! BROWSERLESS_TOKEN ) {
return '<!-- Browserless token is not configured. -->';
}
$target = esc_url_raw( $atts['url'], array( 'http', 'https' ) );
if ( ! $target || ! wp_http_validate_url( $target ) ) {
return '<!-- Invalid screenshot URL. -->';
}
// Restrict destinations in production to prevent server-side request forgery.
$host = wp_parse_url( $target, PHP_URL_HOST );
$allowed_hosts = array( 'example.com', 'www.example.com' );
if ( ! $host || ! in_array( strtolower( $host ), $allowed_hosts, true ) ) {
return '<!-- Screenshot host is not allowed. -->';
}
$request_url = add_query_arg(
'token',
rawurlencode( BROWSERLESS_TOKEN ),
'https://production-sfo.browserless.io/screenshot'
);
$payload = array(
'url' => $target,
'options' => array(
'type' => 'png',
),
);
if ( '1' === (string) $atts['full_page'] ) {
$payload['options']['fullPage'] = true;
}
$response = wp_remote_post(
$request_url,
array(
'timeout' => 60,
'redirection' => 0,
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( $payload ),
)
);
if ( is_wp_error( $response ) ) {
return '<!-- Screenshot request failed. -->';
}
$status = wp_remote_retrieve_response_code( $response );
$content_type = wp_remote_retrieve_header( $response, 'content-type' );
$image_bytes = wp_remote_retrieve_body( $response );
if ( 200 !== $status || 0 !== strpos( (string) $content_type, 'image/' ) || '' === $image_bytes ) {
return '<!-- Screenshot service returned an error or non-image response. -->';
}
// A shortcode should return content. This example embeds bytes as a data URL;
// for repeated or large captures, save the image to controlled storage instead.
$data_uri = 'data:' . sanitize_mime_type( $content_type ) . ';base64,' . base64_encode( $image_bytes );
return sprintf(
'<img loading="lazy" alt="%s" src="%s" />',
esc_attr( $atts['alt'] ),
esc_attr( $data_uri )
);
}
Replace the example host allowlist and endpoint with the host and Browserless endpoint appropriate for your account. Browserless documents account tokens and token-based requests; use the endpoint shown for your account in its REST API documentation. The inline data URL keeps this example compact, but storing the result as a file and returning its URL is generally more suitable for reusable images and page performance.
3. Use the shortcode in a post
After installing and activating the plugin, place the shortcode in a post or page whose editors are allowed to capture the configured host:
[browserless_screenshot url="https://example.com/" alt="Example homepage" full_page="1"]
WordPress’s shortcode callback should return its replacement markup rather than directly printing it. See the WordPress add_shortcode() reference.
4. Production storage and security
Save image bytes instead of embedding a data URL
A data URL increases the HTML size and prevents browsers from caching the screenshot as a separate image resource. For a production integration, write validated image bytes to controlled storage, then render an <img> with that file’s URL. If saving into the WordPress Media Library, use WordPress upload helpers and ensure the upload’s MIME type matches the actual response. Avoid trusting a filename extension or a content type header alone; reject responses that are not successful image data.
Use a cache key based on the normalized target URL and capture options. Store the file URL and refresh it only when the cache expires or the source changes. This is implementation guidance to avoid regenerating the same screenshot on every page view.
Prevent server-side request forgery
Never let anonymous visitors submit arbitrary URLs for your WordPress server to fetch. A malicious target could point at private network services or local addresses. Prefer URLs generated from known WordPress content, use a strict hostname allowlist, reject IP literals and non-HTTP schemes, and protect any capture form with authentication, capability checks, and a nonce. Revalidate redirects and the final destination if your workflow permits redirects.
Keep credentials out of logs and markup
Keep the token in server-side configuration. Do not expose it in shortcodes, rendered HTML, JavaScript, or public REST responses. Since the shared REST request uses a token query parameter, redact query strings from application and proxy logs where possible. Use HTTPS and rotate credentials according to your organization’s secret-management practice.
5. Capture options and readiness
| Need | Browserless setting or approach | Notes |
|---|---|---|
| PNG, JPEG, or WebP output | Set the screenshot type in options | Choose based on image content and downstream use. |
| Full page | fullPage: true |
Captures the full document rather than just the viewport. |
| One element | Set top-level selector with the URL |
Confirm the selector exists and is unique enough for the page. |
| Specific viewport | Use viewport width and height options | Viewport changes layout and responsive breakpoints. |
| Sharper or retina output | Set deviceScaleFactor |
Higher scale creates more pixels and larger images. |
| Crop or clip | Use clipping options | Check that the target coordinates fit the selected viewport. |
| Lazy-loaded content | Set request option scrollPage: true |
Scrolling before capture can trigger lazy loading. |
| Wait for content | Wait for a selector, event, function, or timeout | Prefer a meaningful readiness condition over an arbitrary long delay. |
Refer to the Screenshot API reference for the exact request shape and supported options. Test the selected settings against the actual target page: a capture can succeed while still omitting content that loads later or depends on interaction.
6. India hosting, region, and data handling
An India-based WordPress host makes an outbound request to Browserless, but it does not determine the browser’s execution region or where Browserless stores or forwards data. Browserless’s trust information says shared-fleet browser sessions execute in the region selected by the endpoint, with San Francisco, London, or Amsterdam listed. The same material says telemetry, logs, session replays, saved profiles, and crawl results may remain in US infrastructure. The reviewed documentation did not establish an India shared-cloud execution endpoint. Check the current Browserless Trust Center and endpoint details when choosing a deployment.
If your requirement is to keep browser execution and stored data within infrastructure you control, Browserless describes a self-hosted option for a customer VPC, data center, or on-premises environment. Self-hosting makes your team responsible for infrastructure operations and requires checking applicable licensing. Browserless identifies its open-source Docker image as SSPL-1.0 and says closed-source commercial use or closed-source CI requires a commercial license. Verify current terms and deployment details on the Browserless self-hosted page.
7. Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| WordPress reports a request error | Outbound HTTPS is blocked, DNS or TLS failed, or the request timed out | Check the hosting plan’s outbound network rules, PHP/OpenSSL setup, DNS, and timeout logs. |
| 401 or authorization failure | Missing, incorrect, expired, or malformed token | Confirm the server-side secret and the token format expected by the account endpoint. |
| Non-image response | Browserless returned an API error or an unexpected response | Check status and content type before saving; log a redacted response summary, not credentials. |
| Blank or white screenshot | The target is blocked, not ready, or rendered outside the expected state | Check the target directly, add an appropriate readiness wait, and inspect whether it blocks browser automation. |
| CAPTCHA, 403, or access-denied image | The target site is restricting automated browser access | Treat this as a target-site restriction. Do not assume an ordinary screenshot request will bypass it; confirm permitted access with the site owner. |
| Element missing from capture | Wrong selector, late rendering, lazy loading, or selector not present at capture time | Validate the selector and wait for it; use scrollPage: true when lazy loading is involved. |
| Image is only the viewport | Full-page capture was not enabled | Set fullPage: true in the screenshot options. |
| Image too large or slow to serve | Full page, large viewport, or high device scale factor | Reduce dimensions or scale, select a compressed format where suitable, and cache the generated file. |
| Shortcode prints nothing | Callback returned an empty string due to validation or service failure | Inspect server logs with credentials redacted and surface a safe editor-facing diagnostic outside public page markup. |
8. Performance, reliability, and cost considerations
Screenshot generation adds an external service request to the publishing or page-rendering path. Avoid calling Browserless synchronously for every public page view. Generate on demand in an authenticated administrative workflow, during a background job, or once and cache the result. Set a bounded timeout, handle errors without breaking the page, and retain the last known good image if refresh fails.
Large full-page images and high device scale factors increase transfer size and storage needs. Choose the smallest viewport and format that meet the use case, set a cache expiration based on how often the source changes, and consider whether stale screenshots are acceptable. Track request failures and cache hit rates without recording tokens or sensitive target URLs.
The research dossier does not establish Browserless pricing, India-specific latency, or service performance figures, so this guide makes no cost or speed estimate. Check your Browserless plan and your WordPress host’s outbound-request limits before production use.
9. Or skip the browser setup
If your goal is to capture a page from WordPress without operating the browser workflow, ScreenshotNeo provides a website screenshot API and MCP server. A single GET request returns an image or PDF. Its documented API options include full-page and selector captures, viewport and device presets, waits, custom CSS or JavaScript, and caching. See the ScreenshotNeo API documentation.
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)
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}`);
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed; response headers identify page verdict and billing status.
- An MCP server lets AI agents using Claude, Cursor, or another MCP client take screenshots.
- 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account to get 1,000 screenshots a month without a card.
10. FAQ
Can I return the screenshot directly from the shortcode without saving it?
Yes. The compact example embeds bytes as a data URL, but for repeated display use a stored image URL so the browser can cache the file separately from the page HTML.
Does hosting WordPress in India guarantee the browser runs in India?
No. The WordPress server and Browserless browser execution are separate. The shared endpoint region determines execution, and the reviewed Browserless material does not establish an India shared-cloud endpoint.
Should screenshots be generated when a reader opens a page?
Usually, generate and cache them ahead of the public request or refresh them in a background workflow. That keeps an external browser call from slowing or breaking normal page rendering.
Can Browserless capture a page protected by a CAPTCHA?
A screenshot may show the CAPTCHA or access-denied page. The standard screenshot request should not be treated as a way to evade a target’s restriction; Browserless documents a separate unblock product, whose applicability and permitted use depend on the target and service terms.


