Add headers, footers and page numbers to HTML-to-PDF

Add headers, footers and page numbers to HTML-to-PDF

PDF headers, footers and page numbers: add repeating "Page 1 of 5" footers with Chrome, Puppeteer, Gotenberg or an HTML to PDF API.

Chromium (and every tool built on it) prints headers and footers from separate HTML templates. They are not part of your page, which is why they behave differently from normal HTML.

Quick answer. Pass a footer_html template with the pageNumber and totalPages classes, and give it room with a bottom margin.

curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" -d '{
  "html": "<h1>Report</h1>",
  "margin": {"bottom": "20mm"},
  "footer_html": "<div style=\"font-size:9px;width:100%;text-align:center\">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span></div>"
}' -o report.pdf

The placeholder classes

These are the classes Chromium fills in automatically inside header and footer templates.

class value
pageNumber current page number
totalPages total number of pages
date print date
title document <title>
url document URL
<div style="font-size:9px; width:100%; text-align:center; color:#666;">
  Page <span class="pageNumber"></span> of <span class="totalPages"></span>
</div>

With html-pdf.com

This is the full API call with both a header and a footer. Send the templates as header_html and footer_html, and leave room for them with margin.

curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" -d '{
  "html": "<h1>Quarterly report</h1><p>…</p>",
  "margin": {"top": "25mm", "bottom": "20mm", "left": "15mm", "right": "15mm"},
  "header_html": "<div style=\"font-size:9px;width:100%;padding:0 15mm;display:flex;justify-content:space-between\"><span>Acme Inc.</span><span class=\"date\"></span></div>",
  "footer_html": "<div style=\"font-size:9px;width:100%;text-align:center\">Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span></div>"
}' -o report.pdf

From an AI agent, just ask "Add page numbers in the footer". The MCP tool html_to_pdf takes the same footer_html option.

Pure CSS: @page margin boxes

This section shows how to put page numbers in your stylesheet instead of a template. Modern Chromium (version 131 and later) supports CSS paged-media margin boxes.

@page {
  margin: 20mm;
  @bottom-center { content: "Page " counter(page) " of " counter(pages); font-size: 9px; color: #666; }
  @top-right { content: "Acme Inc."; font-size: 9px; }
}

Send it with "prefer_css_page_size": true so the @page margins are used. No separate header or footer template is needed.

With Puppeteer

This is the same setup in Puppeteer, using displayHeaderFooter and the two templates.

await page.pdf({
  path: "report.pdf",
  format: "A4",
  displayHeaderFooter: true,
  headerTemplate: `<div style="font-size:9px;width:100%;text-align:right;padding-right:15mm"><span class="date"></span></div>`,
  footerTemplate: `<div style="font-size:9px;width:100%;text-align:center">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>`,
  margin: { top: "25mm", bottom: "20mm" },
});

With Gotenberg

This is how to add headers and footers in Gotenberg. Upload header.html and footer.html next to index.html. Each must be a full HTML document.

curl -F files=@index.html -F files=@header.html -F files=@footer.html \
  -F marginTop=1 -F marginBottom=0.8 \
  http://localhost:3000/forms/chromium/convert/html -o out.pdf

Why your header doesn't show (or looks tiny)

These are the most common reasons a header or footer goes missing or looks wrong.

Skip the setup

This is the simplest way to get page numbers without running a browser. Page numbers, headers and footers work out of the box on html-pdf.com with one JSON call.

You get 100 free pages, then from $0.005 per page, and credits never expire. See also CSS page breaks.

Get a free API key · API docs

FAQ

How do I add page numbers to a PDF generated from HTML?

Pass a footer_html template that contains <span class="pageNumber"></span> and <span class="totalPages"></span>, and set a bottom margin so it has room. Chromium fills in the numbers on every page.

Headers and footers render inside the page margin, so with zero margin they are invisible. Also set font-size inline, because the default is tiny and your page CSS does not apply to the template.

Can I add page numbers with CSS only?

Yes. Chrome 131 and later support @page margin boxes, for example @bottom-center { content: "Page " counter(page) " of " counter(pages) }. Send it with prefer_css_page_size set to true.

How do I skip the header on the first page?

Header and footer templates repeat on every page. Render the cover as its own PDF and merge it, or design the header so it also fits the cover.

This page as markdown · text