How to Add Headers and Footers to a Webpage PDF with Puppeteer
Add repeating headers, footers, and page numbers to a webpage PDF with Puppeteer using templates, print margins, and practical fixes.
To add repeating headers and footers to a webpage PDF with Puppeteer, set displayHeaderFooter: true in page.pdf(), provide HTML in headerTemplate and/or footerTemplate, and set top and bottom margins to leave room for them. Use the template classes pageNumber and totalPages for automatic pagination.
Complete example: generate a webpage PDF
This runnable Node.js example opens a page and saves a PDF with the page title in the header and current and total page numbers in the footer. Install Puppeteer with npm install puppeteer; its package includes a compatible browser download in the standard installation flow.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.pdf({
path: 'page.pdf',
format: 'A4',
printBackground: true,
displayHeaderFooter: true,
headerTemplate: `
<div style="font-size: 9px; width: 100%; text-align: center; color: #555;">
<span class="title"></span>
</div>`,
footerTemplate: `
<div style="font-size: 9px; width: 100%; text-align: center; color: #555;">
Page <span class="pageNumber"></span> of
<span class="totalPages"></span>
</div>`,
margin: {
top: '0.7in',
bottom: '0.7in',
left: '0.5in',
right: '0.5in',
},
});
} finally {
await browser.close();
}
})();
Save this as make-pdf.js and run node make-pdf.js. Replace the example URL with the page to print. The margins are illustrative: increase them if your template is taller, and inspect the resulting PDF at the target paper size.
Configure the header and footer templates
Puppeteer renders each template as HTML for the printed page furniture. Set either template independently, or set both. These special classes insert values during PDF generation:
| Class | Inserted value |
|---|---|
date |
Print date |
title |
Document title |
url |
Document URL |
pageNumber |
Current page number |
totalPages |
Total page count |
For example, a compact left-aligned URL header and right-aligned page count can be written as:
headerTemplate: `
<div style="font-size: 8px; width: 100%; padding: 0 0.5in;">
<span class="url"></span>
</div>`,
footerTemplate: `
<div style="font-size: 8px; width: 100%; text-align: right; padding: 0 0.5in;">
<span class="pageNumber"></span> / <span class="totalPages"></span>
</div>`,
Keep the markup and styling simple, and verify how it renders in the generated PDF. Use the documented class names with exact casing; application JavaScript does not need to calculate page counts.
Choose page size, margins, and print styles
page.pdf() uses print CSS media by default. A page that looks correct in the browser viewport can therefore have a different layout in its PDF. Review @media print and @page rules when the output differs from the screen.
format: Choose a paper format such as'A4'or'Letter'. The documented default is Letter. Whenformatis set, it takes priority overwidthandheight.widthandheight: Use explicit dimensions when the document needs a custom page size and you are not relying onformat.landscape: Settruefor landscape orientation.margin: Settopandbottomlarge enough to keep the header and footer clear of page content. The API default leaves margins undefined, so do not assume there is reserved space.preferCSSPageSize: Use this when the document’s CSS@pagesize should take priority.scale: Adjust print scaling if the page content needs to fit differently on the chosen paper.printBackground: Enable this when background graphics and colors from the page should appear in the PDF.
To render screen styles instead of print styles, emulate the screen media type before creating the PDF:
await page.emulateMediaType('screen');
await page.pdf({
path: 'screen-styled.pdf',
displayHeaderFooter: true,
headerTemplate: '<div><span class="title"></span></div>',
footerTemplate: '<div><span class="pageNumber"></span></div>',
margin: { top: '0.6in', bottom: '0.6in' },
});
Print-oriented color adjustment is applied by default. If exact page colors matter, review the CSS -webkit-print-color-adjust property and inspect the PDF output.
Wait for navigation and page content
Navigate to the page before calling page.pdf(). The example uses networkidle2, but pages with continuous background requests may not reach that condition promptly. For pages that render content asynchronously, wait for a known selector or application-ready condition before printing. Puppeteer’s PDF guide says PDF generation waits for fonts by default; still review the result if the page uses custom fonts or loads content dynamically.
await page.goto('https://example.com/report', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('#report-ready');
await page.pdf({ path: 'report.pdf', format: 'A4' });
cURL, Python, and Node.js alternatives
Puppeteer is a Node.js browser automation library, so its header and footer templates are configured in JavaScript. cURL and Python do not call Puppeteer’s page.pdf() directly. If your application exposes a PDF-generation endpoint that runs this Puppeteer configuration, these examples show how to submit a URL to that endpoint; replace the placeholder endpoint and request fields with your own service’s API.
cURL
curl -X POST 'https://your-service.example/pdf' \
-H 'Content-Type: application/json' \
--data '{"url":"https://example.com","headerFooter":true}' \
-o page.pdf
Python
import requests
response = requests.post(
'https://your-service.example/pdf',
json={'url': 'https://example.com', 'headerFooter': True},
timeout=90,
)
response.raise_for_status()
with open('page.pdf', 'wb') as output:
output.write(response.content)
Node.js using Puppeteer
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.pdf({
path: 'page.pdf',
format: 'A4',
displayHeaderFooter: true,
headerTemplate: '<div><span class="title"></span></div>',
footerTemplate: '<div><span class="pageNumber"></span> / <span class="totalPages"></span></div>',
margin: { top: '0.6in', bottom: '0.6in' },
});
} finally {
await browser.close();
}
})();
Or skip the browser setup
If you need a clean capture of a webpage rather than custom repeating PDF headers and footers, ScreenshotNeo provides a one-call website capture API. Its PDF options include paper size, margins, landscape, and page ranges; the header/footer template workflow described above is the Puppeteer approach.
See the ScreenshotNeo API documentation for request options. This cURL request returns a PDF; set format=pdf for PDF output.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-d format=pdf \
-o page.pdf
- Cookie banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before capture; each step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing; response headers identify the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, and other MCP clients. - The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is on every plan.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
Troubleshooting Puppeteer PDF headers and footers
| Symptom | Likely cause | Fix |
|---|---|---|
| No header or footer appears | displayHeaderFooter is false or missing, or the corresponding template is not set. |
Set displayHeaderFooter: true and provide headerTemplate, footerTemplate, or both. |
| Header/footer overlaps the document | There is not enough printable margin; margins are not set by default. | Increase the top or bottom margin to leave space for the template. |
| Page count or title is blank | The template uses an incorrect or differently cased class name. | Use the documented classes exactly: pageNumber, totalPages, title, date, and url. |
| PDF layout differs from the browser | PDF generation uses print media CSS by default. | Check print-specific CSS and @page; call page.emulateMediaType('screen') first if screen styling is intended. |
| Paper dimensions are unexpected | format takes priority over width and height, or CSS page sizing is in effect. |
Choose one sizing approach deliberately and review preferCSSPageSize. |
| Fonts or colors look different | Print rendering and print color adjustment affect appearance. | Allow page fonts to load, review -webkit-print-color-adjust, and inspect the generated PDF. |
| Navigation hangs before PDF generation | A page with ongoing network requests may not reach the selected network-idle condition. | Use an appropriate navigation condition and wait for a specific ready selector when available. |
Performance, reliability, and cost
PDF generation includes browser startup, navigation, page rendering, and PDF writing. For repeated jobs, manage browser and page lifetimes deliberately and always close resources when finished, including on errors. Wait for the page state your document actually needs; waiting on an idle condition can be unsuitable for pages that keep connections open.
Rendering can vary with the page’s print CSS, loaded assets, browser version, and runtime environment. Keep the Puppeteer and browser versions controlled in deployment, use explicit paper and margin settings, and inspect representative output in the target runtime. The documentation defines the options and print behavior but does not promise identical results across operating systems or Chromium versions.
Puppeteer itself is an open-source browser automation library. Your operational cost depends on where you run the browser and how your service provisions CPU, memory, concurrency, and storage; the source material provides no universal runtime benchmark or cost figure. If you use a hosted capture service instead, compare its pricing and whether it supports the PDF layout controls you need.
FAQ
Can I add only a footer?
Yes. Enable displayHeaderFooter and set footerTemplate; the header template is optional.
Can I put the total page count in the footer?
Yes. Add <span class="totalPages"></span> alongside pageNumber.
Does the template repeat on every page?
The header and footer templates are the running page furniture for the generated PDF, so use them for content that should appear across pages.
Can I use my website’s CSS header instead?
Page content can be styled through print CSS, but Puppeteer’s dedicated repeating header and footer are supplied through the PDF template options. Use those templates when you need page metadata such as page number and total pages.


