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

Use a separate HTML file with --header-html, give the PDF enough top margin, and let wkhtmltopdf pass page metadata to that file as query-string values. A small JavaScript subst() function copies those values into elements whose classes are page, topage, title, date, or another supported variable. The result is a header that changes automatically on every page.

The working pattern

wkhtmltopdf renders the header as a separate HTML document. During conversion it appends values such as the current page and total pages to that document’s URL. Your header script reads the query string and fills marked elements. The body document does not need to know the final page count.

  1. Create a header HTML file containing placeholders and the substitution script.
  2. Pass it with --header-html.
  3. Reserve vertical space with --margin-top.
  4. Use --header-spacing to control the gap between the header and body.
  5. Convert the source HTML to PDF and inspect a multi-page result.

Minimal conversion command

wkhtmltopdf 
  --header-html header.html 
  --margin-top 25mm 
  --header-spacing 5 
  input.html output.pdf

The margin is part of the layout, not a cosmetic setting. If it is smaller than the rendered header, the header can overlap the first lines of content or be clipped. If the spacing is excessive, the header may be pushed outside the printable area; reduce it or increase the margin.

Build a dynamic header.html

Save this as header.html. It supports the page number, total page count, document title, local date, and ISO date. Add your own markup and CSS around the placeholders as needed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <script>
    function subst() {
      const vars = {};
      const query = document.location.search.substring(1).split('&');
      for (const item of query) {
        if (!item) continue;
        const pair = item.split('=', 2);
        vars[pair[0]] = decodeURIComponent(pair[1] || '');
      }
      for (const name of ['page', 'topage', 'title', 'date', 'isodate']) {
        for (const el of document.getElementsByClassName(name)) {
          el.textContent = vars[name] || '';
        }
      }
    }
  </script>
</head>
<body style="border:0; margin:0" onload="subst()">
  <table style="width:100%; border-bottom:1px solid #888">
    <tr>
      <td class="title"></td>
      <td style="text-align:right">Page <span class="page"></span> of <span class="topage"></span></td>
    </tr>
  </table>
</body>
</html>

The class names are the API. Keep the subst() call on page load, use textContent for untrusted values, and place the same class on multiple elements if a value must appear more than once. wkhtmltopdf’s documented HTML-header mechanism supplies the metadata; it does not execute a server-side template in your application.

Supported values in the HTML header

Class/token Meaning Typical use
page / [page] Current page number “Page 3”
frompage / [frompage] First page in the current range “Pages 3–7” when ranges are used
topage / [topage] Last page or total page number “of 12”
webpage / [webpage] Web-page address Source identification
section / [section] Current section Section labels
subsection / [subsection] Current subsection Nested navigation
date / [date] Formatted date Human-readable report date
isodate / [isodate] ISO-formatted date Machine-friendly date
time / [time] Time of conversion Build timestamp
title / [title] Page title Document title
doctitle / [doctitle] Document title value Report or book title
sitepage / [sitepage] Page within a site or object Site-level numbering
sitepages / [sitepages] Total pages within that site/object Site-level totals

HTML headers use the class form. Text-only options use bracketed tokens. Do not write [page] inside the HTML file and expect it to be substituted; put a class="page" element there instead.

Use a plain text header when HTML is unnecessary

For a fixed label plus page numbers, the built-in left, center, and right options are simpler and avoid a second HTML document.

wkhtmltopdf 
  --header-left "Project report" 
  --header-right "Page [page] of [topage]" 
  --margin-top 18mm 
  input.html output.pdf

These options also accept tokens such as [frompage], [webpage], [section], [subsection], [date], [isodate], [time], [title], [doctitle], [sitepage], and [sitepages]. Choose HTML when you need branding, tables, images, conditional elements, or custom JavaScript; choose text options for a one-line header.

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

Pass your own dynamic values with –replace

--replace adds named substitutions for header and footer text. Quote values containing spaces or punctuation.

wkhtmltopdf 
  --replace customer "Acme Ltd" 
  --header-right "[customer] — Page [page] of [topage]" 
  --margin-top 18mm 
  input.html output.pdf

This is useful for a customer name, invoice identifier, environment label, or build number that your application already knows. It is intended for header and footer text; it is not a general-purpose variable system for arbitrary body HTML.

Headers, footers, margins, and page geometry

Reserve space deliberately

Measure the header’s rendered height at the fonts and image sizes you deploy. Set --margin-top above that height, then use --header-spacing for a small visual gap. A border or background does not increase the PDF’s available page area; it still consumes the margin region.

Add a dynamic footer

Footers use the corresponding options: --footer-html, --footer-left, --footer-center, --footer-right, --footer-spacing, and --margin-bottom. The same substitution variables and HTML-document approach apply. Give the footer its own margin so body content cannot collide with it.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

Keep layout predictable

  • Use a fixed or carefully constrained header height; long titles can wrap and change it.
  • Prefer absolute or otherwise accessible URLs for images and stylesheets.
  • Keep table widths explicit when the header contains columns.
  • Test at the exact paper size, orientation, and margin settings used in production.

Wait for JavaScript and asynchronous data

JavaScript is enabled by default in the documented command-line behavior, but a header that fetches data asynchronously can run before the data arrives. Increase the delay when a known amount of time is sufficient:

wkhtmltopdf 
  --header-html header.html 
  --javascript-delay 1500 
  --margin-top 25mm 
  input.html output.pdf

A more deterministic pattern is to have the page set a known window.status value after its data and layout are ready, then wait for that value:

wkhtmltopdf 
  --header-html header.html 
  --window-status ready-for-pdf 
  --margin-top 25mm 
  input.html output.pdf

Use a delay for a simple, bounded page. Use --window-status when readiness depends on network requests or client-side rendering. Ensure the status is always set on success; otherwise conversion can wait indefinitely or until the process timeout imposed by your wrapper.

Make assets and local files load reliably

The header is loaded by the wkhtmltopdf process, not by your browser session. A relative image or stylesheet path that works interactively may fail when the header is opened from another directory or a remote worker. Use an absolute, reachable URL or a path valid in the conversion environment. If your deployment restricts local-file access, configure it according to your security policy rather than assuming a desktop default.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #4
The SQL Programming Language: .
  • Used Book in Good Condition

Keep credentials out of query strings and header markup. If the header must display tenant-specific information, pass a controlled value with --replace or generate a temporary header file with permissions limited to the conversion user.

Reproducibility and version management

The upstream wkhtmltopdf repository was archived by its owner on January 2, 2023 and is read-only. That maintenance status does not prevent existing binaries from working, but it increases the importance of pinning the exact binary and testing it in the same operating-system image used in production.

  • Record the wkhtmltopdf version and the operating-system image.
  • Run a regression PDF containing a long title, an image, a page break, and a footer.
  • Compare output after any font, WebKit, container, or binary change.
  • Set an external process timeout and capture stderr for failed jobs.
  • Keep a known-good binary available for rollback.

Troubleshooting dynamic headers

Symptom Likely cause Fix
Header is absent The file cannot be reached or the option points to the wrong path. Use a path or URL accessible to the wkhtmltopdf process, verify permissions, and check stderr.
Header overlaps body text Top margin is smaller than the rendered header. Increase --margin-top; then tune --header-spacing.
Header is clipped or appears outside the page Spacing is excessive for the available margin. Reduce spacing or increase the top margin and retest at the target paper size.
Page or title values are blank Class names do not match, query parsing was removed, or subst() never ran. Use the documented class names, preserve the on-load call, and inspect the generated header file in isolation.
Images or CSS do not appear Relative paths, inaccessible local files, authentication, or blocked resources. Use absolute accessible paths, make required resources available to the worker, and account for local-file restrictions.
Async values are missing Rendering finished before client-side work completed. Increase --javascript-delay or set and wait for a reliable window.status.
Results differ between machines Different binaries, fonts, WebKit behavior, or operating-system libraries. Pin the binary and environment, install the same fonts, and test in deployment.
Conversion hangs A readiness status is never set or a resource never responds. Set status on every success path, enforce an outer timeout, and remove or bound slow dependencies.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is a clean image or PDF of a URL rather than a wkhtmltopdf-specific local pipeline, ScreenshotNeo makes the capture a single API request. It accepts consent banners like a visitor, removes more than 60 known consent platforms plus newsletter popups and chat widgets before capture, and lets you turn each cleanup step off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; each response identifies the result with X-Page-Verdict and X-Billed headers.

For API parameters, authentication, PDF settings, and the complete option list, see the ScreenshotNeo documentation.

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

cURL

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

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
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}`);

ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every feature is included on every plan: full-page and element capture, device presets and custom viewports, dark mode, retina scale, CSS and JavaScript, clicks, waits, request blocking, custom headers and cookies, user-agent, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage API, and an OpenAPI specification. Existing parameter names used by other screenshot APIs also work, which can simplify migration.

Best Value
Programming Is Like Writing A Book. Funny Programmer Codes Coffee & Tea Mug For Computer Programmers, Software Engineers, IT Professionals, Web Designers, Coders, Beginners & Students (11oz)
  • THE PERFECT GIFT IDEA: The perfect gift can be hard to find, but with this unique, not-sold-in-stores coffee and tea mug, you’re sure to give the best gift every time.
  • TREAT YOURSELF OR A FRIEND: Whether you’re buying this high quality mug for yourself, a friend, boss, co-worker, or family member they’re sure to love its distinctive, long-lasting design. It’s a great, multi-functional gift for anyone for any occasion.
  • PREMIUM QUALITY: Our premium, full-color sublimation imprint appears on both sides of this 11 ounce, white ceramic mug. Each mug is crafted from the highest grade ceramic, and all of our designs are printed and sublimated in the United States.
  • MICROWAVE AND DISHWASHER SAFE: This 11 ounce, white ceramic coffee mug has a large, easy-to-grip C-handle and is both microwave and dishwasher safe.
  • SATISFACTION GUARANTEED:Your complete satisfaction is our top priority. We meticulously package our mugs to ensure they arrive on time and in great condition.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; higher plans are Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000. Yearly billing gives two months free. Create a free ScreenshotNeo account to start with the 1,000 monthly shots and no card.

When to choose each approach

  • Choose wkhtmltopdf when you own the HTML input, need its established CLI flags, or must reproduce an existing server-side PDF workflow.
  • Choose ScreenshotNeo when the source is a live URL, consent and popup cleanup matters, you want image or PDF output without managing a browser binary, or an AI agent should perform captures through MCP.
  • Use both when internal reports are rendered locally but public pages, previews, or fallback captures are handled through an API.

Frequently Asked Questions

Can one header file show different content on different page ranges?

Yes. The header receives the current page and range metadata, so your script can branch on the supplied values or display them directly. Keep the layout height stable so a conditional change does not collide with the body.

What happens if a document title contains characters such as an ampersand?

Read the decoded query value and assign it with textContent, as in the template. Do not concatenate it into innerHTML; that avoids treating title text as markup.

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

Is a dynamic header recalculated for every page?

wkhtmltopdf renders the supplied header document as part of the PDF conversion and provides page metadata for substitution. The same header design can therefore display a different current-page value on each page.

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.