PDF and reporting

Generate PDFs with headers, footers and print CSS

The page looks right on screen but printing changes colors, pagination and margin boxes. Chromium header/footer templates are separate documents with restricted styling, not ordinary elements from the page.

3 min readPublished Updated
What you actually see

Backgrounds disappear, content overlaps the footer, `position:fixed` repeats unexpectedly, or page numbers render with the default tiny font.

Reproduce it

  1. 01Print once with screen media and once with print media; compare page breaks and hidden navigation.
  2. 02Enable a footer without increasing bottom margin and inspect the overlap on every page.
  3. 03Place `<span class='pageNumber'>` and `<span class='totalPages'>` in the template; arbitrary page scripts do not run there.
The DIY version

Start with the smallest thing that works

Use print CSS for document layout and Chromium templates only for the margin content. Reserve physical space with PDF margins.

report-pdf.mts
await page.setContent(html, { waitUntil: "load" });
await page.emulateMedia({ media: "print" });
await page.pdf({
  path: "report.pdf",
  format: "A4",
  printBackground: true,
  displayHeaderFooter: true,
  margin: { top: "0.65in", bottom: "0.65in", left: "0.5in", right: "0.5in" },
  headerTemplate: '<div style="font-size:8px;width:100%;text-align:center"><span class="title"></span></div>',
  footerTemplate: '<div style="font-size:8px;width:100%;text-align:center"><span class="pageNumber"></span> / <span class="totalPages"></span></div>',
});
Where it breaks

The call is small. Everything around it is the system.

Margin boxes have a separate CSS world

Header/footer templates do not inherit the page stylesheet. Inline their font size, width and alignment instead of relying on application classes.

The margin is smaller than the template

Chromium does not automatically push document content away from a tall header. The PDF margins are the layout contract.

Screen and print media disagree

A print stylesheet may hide navigation and reflow tables. Choose `media_type` explicitly and test the same value used in production.

One-page fitting defeats pagination

Running page numbers are meaningful on paginated output. `pdf_fit_one_page` creates a custom single sheet and is a poor match for multi-page reports.

Keep the DIY version when

  • A native PDF library already models the document and no browser CSS is required.
  • The PDF is a single controlled template and your existing browser worker has snapshot coverage for pagination.
The recipe

generate PDF with header footer and print CSS

The header and footer are now public PDF parameters, included in the immutable render spec and cache hash. Missing opposite-side templates are suppressed instead of falling back to Chromium defaults.

Capture settings
Enter the source, choose your options, then run the capture.
Result
Your capture will appear here and stay in view.
waiting

No result yet

Complete the settings and run the tool. Images, PDFs, text, and video all preview in this panel.

Send this exact request
cURL, TypeScript and Python are generated from the same configuration as the demo.
curl --fail-with-body "https://api.pagecapture.dev/v1/take" \
  -H "X-Access-Key: $PAGECAPTURE_KEY" \
  -H "Content-Type: application/json" \
  --data '{"html":"<style>@media print{nav{display:none}section{break-inside:avoid}}body{font:16px system-ui}header{background:#eee;padding:24px}section{padding:24px}</style><title>Quarterly report</title><header><h1>Quarterly report</h1></header><section><h2>Summary</h2><p>Browser-rendered PDF with running page numbers.</p></section>","format":"pdf","media_type":"print","pdf_paper_format":"a4","pdf_print_background":true,"pdf_margin_top":0.65,"pdf_margin_bottom":0.65,"pdf_margin_left":0.5,"pdf_margin_right":0.5,"pdf_header_template":"<div style=\"font-size:8px;width:100%;text-align:center\"><span class=\"title\"></span></div>","pdf_footer_template":"<div style=\"font-size:8px;width:100%;text-align:center\"><span class=\"pageNumber\"></span> / <span class=\"totalPages\"></span></div>"}' \
  -o capture.pdf
What you get, and what it costs

Output, limits, cost, failures

Output

A real PDF with selectable page content, inline-styled running header/footer and Chromium's `pageNumber`, `totalPages`, `date`, `title` and `url` substitutions.

Cost

One successful, non-cached capture costs one credit. Failures, including platform failures, cost zero; cache hits cost zero.

1 creditper successful non-cached capture

Limits

  • Header and footer templates have a combined 128 KiB budget.
  • Templates do not execute JavaScript and do not inherit page CSS.
  • The maximum custom fit-one-page dimension is 200 inches; ordinary reports should remain paginated.

Common errors

request_not_validnever billed

A PDF option is sent with a non-PDF format or a template exceeds its byte budget.

HTTP 400 · terminal

timeout_errornever billed

Remote assets or readiness rules consume the print job budget.

HTTP 500 · retryable

Try it against your own page

The runner above carries this exact configuration into the playground — no retyping, no starting over.

Return to the configured runner
Tested and reviewed

Written by PageCapture Engineering. Reviewed by PageCapture PDF maintainers. Verified 2026-08-12 by running the published code against a fixture.

The new header/footer parameters are contract-tested, normalized into the render spec, included in cache identity and type-checked against Playwright's PDF options; recipe snippets pass syntax validation.

engine-crunknown-pwunknown-f2026-07-1-b2026-07-1StatusPDF rendering options referenceEditorial method

Sources and verification basis

Sources support the browser and API behaviors named above. PageCapture-specific limits and billing are taken from the public contract; external sources are used for the underlying browser behavior.

  1. page.pdf()Playwright

    Defines Chromium PDF options and header/footer template classes.

  2. Defines page-oriented CSS concepts and fragmentation context.

  3. PDF optionsPageCapture

    Canonical public parameter names, types and limits.