Skip to main content

Screenshot HTML

Converts uploaded HTML content into an image using Headless Chromium.

Unlike the URL route, this endpoint renders files provided directly in the request body. It requires an index.html file to serve as the entry point. You may also include additional assets (CSS, images, fonts) in the multipart/form-data request, which can be referenced in your HTML using relative paths.

Configuration

You can configure the Chromium module behavior. See the Chromium module configuration for details.

Basics

POST/forms/chromium/screenshot/html
Gotenberg-Output-Filenamestring
The filename of the resulting file - Gotenberg automatically appends the file extension. Defaults to a random UUID filename.
Gotenberg-Tracestring
A custom request ID to identify the request in the logs; overrides the default UUID.
index.htmlfilerequired
An HTML file named 'index.html' to convert into an image.
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
-o my.jpeg
The screenshot has been successfully created.
Content-Disposition: attachment; filename={output-filename.ext}
Content-Type: {content-type}
Content-Length: {content-length}
Gotenberg-Trace: {trace}
Body: {output-file}

Assets

You can send local files - such as images, fonts, and stylesheets - alongside your HTML to create a rich document.

cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html
--form files=@/path/to/img.png \
-o my.jpeg

When you upload files to Gotenberg, they are stored in a single temporary directory. Consequently, your HTML must reference these assets by filename only, regardless of their original folder structure on your machine.

Do not use absolute paths (/img.png) or subdirectories (./assets/img.png).

✅ Correct Reference

Since index.html and logo.png sit side-by-side in the container:

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My PDF</title>
</head>
<body>
<h1>Hello world!</h1>
<img src="logo.png" />
</body>
</html>

❌ Incorrect Reference

Gotenberg does not recreate your local folder structure (e.g., images/) inside the container.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My PDF</title>
</head>
<body>
<h1>Hello world!</h1>
<img src="/images/logo.png" />
</body>
</html>
Remote Assets

If your HTML references remote URLs (e.g., CDNs, Google Fonts, localhost):

  • DNS & Firewalls: Ensure the container can resolve the domain name and that no firewalls are blocking the connection from the container's IP.
  • Localhost: If you are trying to convert a page running on the host machine (e.g., http://localhost:8080), you cannot use localhost inside the container. You must use the host's network IP or a special Docker DNS name like host.docker.internal (depending on your OS and Docker version).
Base64

For small assets, embedding them directly as Base64 data URIs eliminates network dependencies entirely.

The Base Tag

Avoid using the HTML <base> element. It alters how relative paths are resolved and often breaks the link between your HTML and the uploaded local assets.

Rendering Behavior

widthnumber
The device screen width in pixels.
Default:800
heightnumber
The device screen height in pixels.
Default:600
clipboolean
Define whether to clip the screenshot according to the device dimensions.
Default:false
formatenum
The image compression format. Options: 'png', 'jpeg', 'webp'.
Default:png
qualitynumber
The compression quality (0-100). Only used if format is 'jpeg'.
Default:100
omitBackgroundboolean
Hides the default white background to allow transparency.
Default:false
optimizeForSpeedboolean
Optimizes image encoding for speed rather than resulting file size.
Default:false
emulatedMediaTypeenum
Media type to emulate ('screen' or 'print').
Default:print
waitDelaystring
Duration to wait before converting (e.g. '5s').
Default:None
waitForExpressionstring
Waits until this JavaScript expression returns true.
Default:None
waitForSelectorstring
Waits until the given selector (e.g., '#id') is visible on the page.
Default:None
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
--form width=1280 \
--form height=720 \
--form format=jpeg \
--form quality=85 \
--form optimizeForSpeed=true \
-o my.jpeg

Viewport & Layout

The viewport defines the image. Unlike PDFs which paginate content, screenshots capture the browser's viewport exactly as rendered.

  • Resolution: Ensure you set the width and height form fields to match your desired target device (e.g., 1920x1080 for desktop, 375x812 for mobile).
  • If your screenshot's content is repeated and clipped, consider setting the skipNetworkIdleEvent form field to false (see issue #1065).
tip

If you are simulating a mobile device, remember to also set the userAgent to match a mobile browser, or the site might serve the desktop version.

Image Format

Choosing the right output format impacts quality and file size:

  • PNG: Best for screenshots containing text, UI elements, or flat colors. It is lossless, ensuring text remains sharp.
  • JPEG: Best for screenshots containing photographs or complex gradients. Use the quality form field to balance file size against visual fidelity (default is 100).
  • WebP: Offers a modern balance of compression and quality, usually superior to JPEG.

JavaScript & Dynamic Content

Chromium captures what is currently visible. If the page relies on JavaScript to render data, charts, or external content, the conversion might trigger before the rendering is complete, resulting in blank or incomplete sections.

Cheatsheets

If the content is generated dynamically:

  1. Use the waitDelay form field to add a fixed pause before conversion.
  2. For more precision, use the waitForExpression form field to trigger the conversion only when a specific JavaScript condition (e.g., window.status === 'ready') is met.

Wait Delay

Use this as a fallback when you cannot modify the target page's code. It forces Gotenberg to wait for a fixed duration before rendering, giving JavaScript time to finish execution.

Reliability Note

This method is "brute force". If the page loads faster, time is wasted. If it loads slower, the image will be incomplete. Use explicit waits (Expression or Selector) whenever possible.

cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
--form waitDelay=5s \
-o my.jpeg

Wait For Expression

This is the most robust method for synchronization. It pauses the conversion process until a specific JavaScript expression evaluates to true within the page context. This ensures the image is generated exactly when your data is ready.

Example: Client-side logic

// Inside your HTML page.
window.status = "loading";

fetchData().then(() => {
renderCharts();
// Signal to Gotenberg that the page is ready.
window.status = "ready";
});
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
-- form 'waitForExpression=window.status === '\''ready'\''' \
-o my.jpeg

Wait For Selector

Ideally suited for Single Page Applications (SPAs) or frameworks like React/Vue. This method delays the conversion until a specific HTML element - identified by a CSS selector - appears in the DOM.

Example: Dynamic Element Injection

// Inside your HTML page.
await heavyCalculation();

const completionMarker = document.createElement("div");
completionMarker.id = "app-ready"; // The selector we wait for.
document.body.appendChild(completionMarker);
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
--form 'waitForSelector=#app-ready' \
-o my.jpeg

HTTP & Networking

cookiesjson
Array of cookies to add to the request.
Default:None
extraHttpHeadersjson
Custom HTTP headers for the request.
Default:None
userAgentstring
Overrides the default User-Agent header.
Default:None
skipNetworkIdleEventboolean
Does not wait for Chromium network to be idle.
Default:true
failOnHttpStatusCodesjson
Returns a 409 Conflict if Gotenberg receives these codes for the main URL.
Default:[499,599]
failOnResourceHttpStatusCodesjson
Returns a 409 Conflict if Gotenberg receives these codes for at least one resource (images/css/etc.).
Default:None
ignoreResourceHttpStatusDomainsjson
Excludes resources from failOnResourceHttpStatusCodes checks based on their hostname.
Default:None
failOnResourceLoadingFailedboolean
Returns a 400 Bad Request if assets (images/css/etc.) fail to load due to network errors.
Default:false
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
--form 'userAgent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)"' \
--form skipNetworkIdleEvent=false \
--form failOnResourceLoadingFailed=true \
-o my.jpeg

Cookies

The cookies form field accepts a JSON-formatted array of cookie objects. It allows you to authenticate requests or maintain session state during the conversion process.

Cookie Object Schema

KeyDescriptionDefault
nameThe name of the cookie.Required
valueThe value of the cookie.Required
domainThe domain the cookie applies to (e.g., example.com).Required
pathThe URL path the cookie applies to.None
secureIf true, the cookie is only sent over HTTPS.None
httpOnlyIf true, the cookie is inaccessible to JavaScript (document.cookie).None
sameSiteControls cross-site behavior. Values: "Strict", "Lax", "None".None

Session Lifecycle

Cookies are transient and automatically expire when the request reaches its time limit.

To ensure strict isolation between conversions (preventing data leakage), you can configure the API to explicitly clear the cookie jar after every request. See API Configuration for details.

cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
--form 'cookies=[{"name":"yummy_cookie","value":"choco","domain":"theyummycookie.com"}]' \
-o my.jpeg

HTTP Headers

The extraHttpHeaders form field accepts a JSON-formatted object representing the HTTP headers to send with every request.

Schema

This is a key-value map where:

  • Key: The header name (e.g., Authorization, X-Custom-Header).
  • Value: The header value (string).
Scoped Headers

By default, headers are sent with every request made by the browser (including images, stylesheets, and scripts).

To restrict a header to specific URLs, append a ;scope= token containing a Regular Expression. Gotenberg will process this token and only send the header if the target URL matches the regex.

Example: "X-Internal-Token": "secret-123;scope=.*\\.internal\\.api"

  • Matches: https://data.internal.api/v1 (Header sent)
  • Ignores: https://google.com/fonts (Header NOT sent)

Note: The scope token is stripped before the header is sent to the server.

cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
--form-string 'extraHttpHeaders={"X-Header":"value","X-Scoped-Header":"value;scope=https?:\/\/([a-zA-Z0-9-]+\.)*domain\.com\/.*"}' \
-o my.jpeg

Invalid HTTP Status Codes

You can configure Gotenberg to return a 409 Conflict error if the main page or its resources return specific HTTP status codes.

These fields accept a JSON-formatted array of integers.

FieldDescription
failOnHttpStatusCodesFails if the main page URL returns a matching code.
failOnResourceHttpStatusCodesFails if any asset (image, CSS, script) returns a matching code.

Status Code Ranges

You can define ranges using the X99 notation:

  • 499 matches every code from 400 to 499.
  • 599 matches every code from 500 to 599.

Domain Exclusions

Use ignoreResourceHttpStatusDomains to prevent failures for specific third-party assets (e.g., analytics or tracking scripts that might fail without affecting the PDF).

Matching Rules: Gotenberg matches if the asset's hostname equals or is a subdomain of the entry.

Input values are automatically normalized (trimmed, lowercased, port/scheme removed):

  • example.com
  • *.example.com or .example.com
  • example.com:443 (port is ignored)
  • https://example.com/path (scheme/path are ignored)
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
--form 'failOnHttpStatusCodes=[499,599]' \
--form 'failOnResourceHttpStatusCodes=[499,599]' \
--form 'ignoreResourceHttpStatusDomains=["sentry-cdn.com","analytics.example.com"]' \
-o my.jpeg
The main page or one of its resources returned an invalid status code.
Content-Type: text/plain; charset=UTF-8
Gotenberg-Trace: {trace}
Body: Invalid HTTP status code from the main page: 400: Bad Request

Network Errors

Gotenberg automatically validates the connection to the main page URL. If the browser encounters any of the following critical network errors, the API immediately returns 400 Bad Request to indicate the page could not be reached.

Critical Error List

  • net::ERR_CONNECTION_CLOSED
  • net::ERR_CONNECTION_RESET
  • net::ERR_CONNECTION_REFUSED
  • net::ERR_CONNECTION_ABORTED
  • net::ERR_CONNECTION_FAILED
  • net::ERR_NAME_NOT_RESOLVED
  • net::ERR_INTERNET_DISCONNECTED
  • net::ERR_ADDRESS_UNREACHABLE
  • net::ERR_BLOCKED_BY_CLIENT
  • net::ERR_BLOCKED_BY_RESPONSE
  • net::ERR_FILE_NOT_FOUND
  • net::ERR_HTTP2_PROTOCOL_ERROR
Strict Resource Validation

By default, if an image or stylesheet fails to load (e.g., a broken 404 image link), the PDF is still generated with a missing asset icon.

To force the conversion to fail completely when any resource encounters a network error, set the failOnResourceLoadingFailed form field to true.

Console

failOnConsoleExceptionsboolean
Returns a 409 Conflict response if there are exceptions in the Chromium console.
Default:false
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/html \
--form files=@/path/to/index.html \
--form failOnConsoleExceptions=true \
-o my.jpeg
Exceptions in the Chromium console.
Content-Type: text/plain; charset=UTF-8
Gotenberg-Trace: {trace}
Body:

Chromium console exceptions:

exception "Uncaught" (17:10): Error: Exception 1
at file:///tmp/db09d2c8-31e3-4058-9923-c2705350f2b3/index.html:18:11;
exception "Uncaught" (20:10): Error: Exception 2
at file:///tmp/db09d2c8-31e3-4058-9923-c2705350f2b3/index.html:21:11: