Renderpaper
Log in Start free

Repeat a header on every page of a PDF

If you arrived here from an error like

Chromium failed to print the PDF; this usually happens when the page is too large

you were probably trying to put your document's header into Chromium's headerTemplate option, and you inlined your stylesheet to make it look right.

That error is the second wall. There are two, and both are closed.

Why headerTemplate cannot be styled

Chromium renders headerTemplate and footerTemplate in an isolated context that cannot reach the page's stylesheet. Whatever your build pipeline injects — Vite, Tailwind, a linked CSS file — never arrives, so the header renders as unstyled text.

The obvious fix is to inline the built CSS into the header template itself. That is what produces the error above: the header template has a size limit, and a real stylesheet blows past it. Gotenberg's maintainer has closed this as unfixable, because it is Chromium's behaviour rather than a wrapper's bug.

Renderpaper has no header or footer options at all, so this route does not exist here either. What follows works in any Chromium-based renderer, including this one.

What does not work: position: fixed

This is everybody's first guess, so it is worth recording precisely what it does.

.fixed-header { position: fixed; top: 0; left: 0; right: 0; }
.fixed-footer { position: fixed; bottom: -11mm; left: 0; right: 0; }

Rendered against a 90-row table across three A4 pages, then measured with pdftotext -bbox and looked at:

page 1 page 2 page 3
header y 32.4–55.8 y 32.4–55.8 y 32.4–55.8
first content row y 36.1 y 36.9 y 36.9
footer absent y 51.8 — the top of the page y 51.8

The header does repeat. That is not the problem. The problem is that a fixed element is out of normal flow, so it paints over the content instead of pushing it down: on every page the header sits on top of the first table row, and both are unreadable. The footer is worse — it never appears on page one, and on later pages bottom resolves against the wrong box, landing it at the top of the page across the rows.

A screenshot makes it obvious in a way coordinates do not: page two has "Statement 2026-0142" and "Line item 036" printed on top of each other.

What works: table-header-group

Wrap the document in an outer table. Put the repeating header in <thead> and the repeating footer in <tfoot>.

<table>
  <thead>
    <tr><td colspan="3" class="docheader">
      <strong>Statement 2026-0142</strong> — Northwind Trading AB
    </td></tr>
  </thead>

  <tfoot>
    <tr><td colspan="3" class="docfooter">
      Payment within 30 days · Reference 2026-0142
    </td></tr>
  </tfoot>

  <tbody>
    <tr><td colspan="3">
      <!-- the whole document body goes here -->
    </td></tr>
  </tbody>
</table>
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
tr    { break-inside: avoid; }

Three things follow from being in normal flow, and they are the whole reason this works:

  1. The repeated header pushes content down rather than painting over it.
  2. Your own stylesheet applies, because this is part of the document. Tailwind, web fonts, CSS variables — all of it behaves normally.
  3. There is no size ceiling, because there is no header template to overflow.

Measured on the same 90-row document:

page 1 page 2 page 3
header y 32.4–55.8 y 32.4–55.8 y 32.4–55.8
first content row y 59.4 y 55.6 y 55.6
footer y 783.0 y 783.0 y 583.5

No overlap on any page. The header repeats, the content starts below it, and the footer sits at the foot.

The caveat, stated plainly

Look at the footer on page three: y 583 rather than 783. On the final page the tfoot sits immediately after the last row rather than pinned to the bottom of the sheet, so there is visible white space beneath it.

This is inherent to table-footer-group and there is no CSS fix. If a bottom-anchored final footer matters more than a repeating one, this technique is the wrong choice.

Page numbers are a separate problem with a better answer

If what you actually want in the repeating header is a page number, thead cannot give you one — it is the same content on every page. Chromium does support CSS margin boxes, which can:

@page {
  @bottom-center { content: "Page " counter(page) " of " counter(pages); }
}

That is written up in page numbers in a PDF, and it composes with this technique — a repeating thead header plus a margin-box page number in the same document.

Verification

Both variants were rendered through Renderpaper on 2026-08-08 at 90 rows and A4, producing three pages each. Coordinates come from pdftotext -bbox; both page two renders were rasterised and inspected. The failure table above is measurement, not recollection.

Last updated 2026-08-08.