# 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.
```bash
curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" -d '{
"html": "
Report
",
"margin": {"bottom": "20mm"},
"footer_html": "Page of
"
}' -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 `` |
| `url` | document URL |
```html
Page of
```
## 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`.
```bash
curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" -d '{
"html": "Quarterly report
…
",
"margin": {"top": "25mm", "bottom": "20mm", "left": "15mm", "right": "15mm"},
"header_html": "Acme Inc.
",
"footer_html": "Page of
"
}' -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.
```css
@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.
```js
await page.pdf({
path: "report.pdf",
format: "A4",
displayHeaderFooter: true,
headerTemplate: `
`,
footerTemplate: `Page of
`,
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.
```bash
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.
- **No margin.** Headers render *inside* the page margin. Zero margin means an invisible header. Give at least ~15-25 mm.
- **Default font size is very small.** Always set `font-size` inline (8-10px is typical).
- **Page CSS doesn't apply.** The template doesn't see your stylesheet or web fonts. Use inline styles, and stick to system fonts or embed fonts as data URIs.
- **No background colors.** Add `-webkit-print-color-adjust: exact` to the element.
- **External images don't load.** Use a base64 `data:` URI for logos in headers.
- **Different first page?** Templates repeat on every page. For a cover without a header, render the cover as its own PDF and merge, or design the header so it also fits the cover.
## 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](https://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](https://html-pdf.com/html-to-pdf-page-breaks).
[Get a free API key](https://html-pdf.com/login) · [API docs](https://html-pdf.com/docs)
## FAQ
### How do I add page numbers to a PDF generated from HTML?
Pass a `footer_html` template that contains `` and ``, and set a bottom margin so it has room. Chromium fills in the numbers on every page.
### Why is my Puppeteer header or footer not showing?
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.