ScreenshotNeo

BlogHTML to image & PDF

How to Add a Header to a PDF Generated from HTML

Add a repeating header to an HTML-generated PDF with Puppeteer, or use your renderer’s native header support. Learn about margins, page numbers, alternatives, and common fixes.

By the ScreenshotNeo team30 September 202610 min read

How to Add a Header to a PDF Generated from HTML

To add a repeating header to a PDF generated from HTML, use the header feature of the PDF renderer that processes the HTML. In Puppeteer, enable displayHeaderFooter, provide a headerTemplate, and reserve sufficient top margin. The header API is renderer-specific: Puppeteer settings do not apply to wkhtmltopdf, Prince, or WeasyPrint.

This guide shows a Puppeteer implementation, explains the layout details that affect the result, and summarizes the documented approaches in other renderers. If you are generating a PDF from a web page and do not need a custom repeating header, ScreenshotNeo can capture a page as a PDF through a single API request; see the option below.

1. Identify the renderer before changing the HTML

The browser or library that turns your HTML into a PDF determines how headers work. Find the code path that creates the PDF, then confirm its package and installed version. A framework or service may wrap the renderer, so the call you need to change might live in a helper rather than the route or component that builds the HTML.

For a Puppeteer application, locate the call to page.pdf(). Puppeteer accepts header and footer templates as PDF options. Other renderers have their own APIs or CSS paged-media features, covered in the renderer comparison.

2. Add a repeating header with Puppeteer

Here is a complete Node.js example. It opens a page, waits for the document to load, and saves a PDF with a title header and a page-number footer. Install Puppeteer in your project with npm install puppeteer, save the example as make-pdf.mjs, and run node make-pdf.mjs.

A renderer’s header feature repeats the header across PDF pages while keeping page content in the document body.
A renderer’s header feature repeats the header across PDF pages while keeping page content in the document body.
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });

try {
  const page = await browser.newPage();
  await page.setContent(`
    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
        <style>
          body { font: 16px/1.5 sans-serif; }
          h1 { break-before: page; }
        </style>
      </head>
      <body>
        <h1>Quarterly report</h1>
        <p>Replace this sample with your document HTML.</p>
      </body>
    </html>
  `, { waitUntil: 'networkidle0' });

  await page.pdf({
    path: 'output.pdf',
    format: 'A4',
    printBackground: true,
    displayHeaderFooter: true,
    headerTemplate: `
      <div style="width:100%; padding:0 20mm; font:9px sans-serif; color:#444; text-align:left;">
        Quarterly report
      </div>
    `,
    footerTemplate: `
      <div style="width:100%; padding:0 20mm; font:9px sans-serif; color:#444; text-align:right;">
        Page <span class="pageNumber"></span> of <span class="totalPages"></span>
      </div>
    `,
    margin: {
      top: '24mm',
      right: '20mm',
      bottom: '18mm',
      left: '20mm'
    }
  });
} finally {
  await browser.close();
}

The HTML inside the templates is illustrative. Adjust its styles, content, and margins for your page size and design. Puppeteer documents PDF options including displayHeaderFooter, template markup, margins, and page-number template classes. The values in this example are starting points, not universal dimensions. See the Puppeteer PDFOptions reference.

What each setting does

  • displayHeaderFooter: true turns on the header and footer regions. Supplying templates without enabling this option does not display them.
  • headerTemplate and footerTemplate accept HTML markup used in the page header and footer areas.
  • pageNumber and totalPages are documented template classes for displaying the current page and page count.
  • margin.top and margin.bottom reserve room for header and footer content. Set them according to the actual rendered content and paper size.
  • format selects a paper format such as A4. You can use explicit width and height instead when your output needs custom dimensions.
  • printBackground includes background graphics and colors in the PDF; use it when the design depends on them.

The header repeats on pages generated by the PDF operation. If your document has different sections or needs different headers on selected pages, verify that the renderer’s features and your chosen method support that layout before relying on one template.

3. Reserve space and check print styling

Puppeteer generates PDFs using the print CSS media type by default. That means print-specific rules such as @media print and @page can affect the document even if it looked correct in a browser window. The header template is separate from the page’s ordinary body markup, so a heading placed at the top of the HTML body is not the same as a repeating PDF header.

Reserve enough page margin for the rendered header so it does not collide with body content.
Reserve enough page margin for the rendered header so it does not collide with body content.

Use the margin to keep body content clear of the header. There is no single correct top margin: it depends on the paper size, template height, font, padding, and desired gap. Generate the PDF, inspect it, and increase the top margin if the first body line overlaps the header. Check a later page too, since page breaks can expose problems that are not visible on page one.

If the document must use screen styles rather than print styles, Puppeteer documents emulating the screen media type before creating the PDF:

await page.emulateMediaType('screen');
await page.pdf({
  path: 'output.pdf',
  displayHeaderFooter: true,
  headerTemplate: '<div style="font-size:9px">Quarterly report</div>',
  margin: { top: '24mm' }
});

Choose screen media deliberately. For print output, leave the default in place and adapt the document’s print CSS instead. The Puppeteer Page.pdf() reference documents print media as the default and the screen-media alternative.

4. Choose the right header content

A static report title is straightforward: put it directly in the template. A page counter can use Puppeteer’s documented classes. For document data such as a customer name or report date, construct the template from values your application has already validated and escaped for HTML. Avoid inserting untrusted strings as markup.

Keep template markup simple. Use inline styles so the header’s appearance is defined where it is rendered, and avoid assumptions that the page’s stylesheets or scripts will style the separate header region. For a logo or other image, use a source that the PDF renderer can load in its environment, and verify the result in the PDF. The dossier documents the template mechanism, but it does not guarantee behavior for every external asset, font, or template design.

If the header becomes taller because of a long title or wrapped content, the reserved margin must still provide adequate room. Consider shortening or constraining dynamic text, then check long and short values. A header that fits for one report may overlap body content in another.

5. Other HTML-to-PDF renderers

Use the method that belongs to your installed renderer. These mechanisms are not interchangeable:

Renderer Documented approach Good fit when
Puppeteer displayHeaderFooter, HTML templates, page-number classes, and PDF margins Your application creates PDFs through Puppeteer and you want to set the header in its API call.
wkhtmltopdf Command-line header and footer options, HTML header/footer documents, and replacement placeholders Your application already uses wkhtmltopdf and its documented options fit the header you need.
Prince CSS paged-media page-margin boxes and generated content You want page-margin layout and generated content driven by CSS.
WeasyPrint Running elements can place HTML boxes in page margins Your application uses WeasyPrint and its running-element behavior fits the document. Check the installed release’s API notes, including the documented limitation on the element() start parameter.

For wkhtmltopdf, consult the usage documentation for the installed build; header and footer configuration includes text or HTML and page placeholders. Start with its usage reference. For Prince, page-margin boxes and generated content support running headers, footers, and page numbers; see the paged media documentation. WeasyPrint documents running elements and page-margin placement in its API reference. Check each tool’s documentation for the version you run.

When choosing among these approaches, first consider the renderer your application already uses. Then consider whether the header is static or derived from document content, whether you need page counters or page-specific layout, and whether your implementation should be expressed through a renderer API or CSS paged-media rules. The documented capabilities do not establish one universally best renderer.

6. Troubleshooting common header problems

Symptom Likely cause What to check
Header does not appear Header/footer display is disabled, the options are passed to the wrong PDF call, or a wrapper does not forward them. Confirm the actual renderer, locate the call that writes the PDF, and enable its header feature. In Puppeteer, check displayHeaderFooter: true.
Body overlaps the header The reserved top margin is too small for the rendered header and gap. Increase the top margin to fit the actual template, then inspect the first and later pages.
Header appears once in the body, not on every page The title is ordinary HTML content rather than a repeating header feature. Move it into the renderer’s header mechanism or use the renderer’s paged-media feature.
Fonts, spacing, or page breaks differ from the browser Puppeteer uses print media by default and print CSS may change layout. Review print styles and @page; use screen emulation only if screen styling is intended.
Page number placeholders show as text or are absent The template class spelling or renderer-specific syntax is wrong, or the renderer is not Puppeteer. Use the placeholders documented for the installed renderer and check the template passed to the PDF call.
Long titles collide or wrap unexpectedly Dynamic header content is wider or taller than the sample content. Test representative long values, constrain or shorten content, and reserve adequate margin.
CSS header rules have no effect The renderer uses an API template, while the CSS rule targets a different mechanism, or the selected renderer does not support that rule. Use the renderer’s documented header mechanism and confirm support in its version-specific documentation.

7. Reliability, performance, and cost considerations

PDF generation depends on the rendering process completing with the page content and assets available. In Puppeteer, the example waits for networkidle0 before printing; this is one possible readiness condition, not a guarantee that every site’s dynamic content is finished. Choose a readiness strategy that matches how your page loads, and avoid waiting indefinitely on a page that keeps background requests open.

Headers add layout content to each page. Keep them concise, use a stable page format, and inspect long documents and page breaks. For repeatable results, make the document’s print styles, fonts, and asset availability predictable in the renderer’s environment. A change in content length can alter pagination, so page counts and the location of breaks may vary as the document changes.

Cost depends on the renderer and how your application runs it; the cited renderer documentation does not provide a universal per-PDF cost or performance benchmark. Measure generation time and resource use in your own environment with representative documents. If you need a hosted service instead of maintaining browser setup, ScreenshotNeo charges only for clean shots under the product facts supplied for this article; the PDF endpoint and plan details are described below. A screenshot or page PDF service is a different workflow from arbitrary HTML-to-PDF rendering with a custom Puppeteer header template.

Or skip the browser setup

If your input is a web page and a PDF capture is enough, ScreenshotNeo offers a PDF endpoint call. Its one-GET API accepts a URL and returns a screenshot or PDF. This is not a way to apply Puppeteer’s custom header template to arbitrary HTML; use the renderer-specific code above when you need that control. See the ScreenshotNeo documentation for API details.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o page.pdf
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com", "format": "pdf"},
    timeout=90,
)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com',
  format: 'pdf'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned HTTP ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('page.pdf', Buffer.from(await res.arrayBuffer())));

Use format=pdf when requesting a PDF. ScreenshotNeo also accepts the parameter names used by other screenshot APIs, which can make switching easier. Before capture, it accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

The free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan. Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month, with no card.

FAQ

Can I add a header using only HTML?

A normal heading in the HTML body usually appears where it occurs in the document. For a repeating page header, use the PDF renderer’s header feature or supported paged-media CSS.

Can the header include page numbers?

Yes, if the renderer supports page counters. Puppeteer documents the pageNumber and totalPages template classes; other tools use their own syntax.

Why does the header work in one PDF tool but not another?

Header configuration belongs to the renderer. Confirm which program creates the PDF and use the API or CSS features documented for that renderer and version.

Does ScreenshotNeo support a custom Puppeteer header template?

The product facts used here describe URL-based screenshots and PDF captures, not a custom Puppeteer template API. Use Puppeteer when the PDF needs a header you define in its renderer options.