Headers and footers in HTML to PDF
A letterhead is the usual reason to want a header, and the usual way to write one is an <img> pointing at the logo. Printed through headless Chromium that image does not appear, and it is not slow or blocked: it is never requested at all.
Measured on Chromium 151.0.7922.34 against a server that counted every request it received.
The header is printed with the network closed
one document, several header templates
drawn requests reaching the server
<img src="http://.../logo.png"> no 0
<img src="data:image/png;base64,..."> yes 1 (none needed)
<link rel="stylesheet" href="..."> render failed
<style> inside the header yes 0
style="" attribute yes 0
the same URL, from the body yes 1The last line is the control and it is why this is a statement about the header rather than about the server. The same URL, requested from the body of the same document in the same run, arrives and draws. Nothing is unreachable; the header is simply not asking.
So an image by URL fails silently, which is the ordinary way this is met: the page prints, everything else is right, and the corner where the logo should be is empty.
A stylesheet link does not fail silently, it ends the render
The one case that is not quiet. <link rel="stylesheet"> in a header template does not draw unstyled text. It stops the print:
Protocol error (Page.printToPDF): Printing failedNo page is produced and nothing in the message mentions the header, the link or the stylesheet. A document that printed yesterday and does not today, after a header was given a class, is this.
What a header can use
Everything that needs no fetch. Styles work as an attribute or in a <style> tag inside the template itself, and an image works as a data URI:
<img src="data:image/png;base64,iVBORw0KGgoAAA..."
style="height:12mm"><span style="font-size:9pt; color:#555">ACME LTD</span>A logo is usually small enough that inlining it costs little, and the cost is paid once per page rather than once per document. Measure it if the header carries a photograph: the bytes land in every page of the file.
The template does not inherit your document’s stylesheet either, which is a separate thing from the network and is measured in page numbers in a PDF from headless Chrome, along with the default size the text draws at if you set none.
Checking it on your own file
Whether an image reached the file is a count, and reading it needs nothing installed:
node -e "const s=require('fs').readFileSync('invoice.pdf','latin1'); console.log(((s.match(/\/Subtype *\/Image/g)||[]).length) + ' embedded images')"1 embedded imagesZero, with a logo you can see in the browser, is the header having asked for nothing. The number counts the whole file, so print one page while you are checking.
How we handle this
PaperPony takes header_html and footer_html on the request and passes them to the same mechanism, so all of the above applies: inline your styles, and inline your logo as a data URI.
The stylesheet case is worse with us than the measurement above suggests, and it is worth saying so. A print that fails this way reaches our API as an ordinary render failure, is treated as retryable, and is attempted three times before the job ends with render_failed and the words the renderer could not produce output. Nothing in that names the header. If a render started failing after a change to yours, that is the first place to look rather than the last.