Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To place an element at the bottom of each A4 page in a Puppeteer PDF, define the paper as A4, apply the positioning rule in print CSS, and use position: fixed. Reserve enough bottom margin so flowing content cannot overlap the element. For a true PDF footer—especially one containing page numbers—use Puppeteer’s footerTemplate instead. A fixed element repeats on every printed page; it does not target only the final page.

Choose the right bottom-positioning method

Requirement Recommended method Why
An ordinary HTML element anchored to the printed page Print CSS with position: fixed The element is positioned relative to each printed page and repeats on every page.
A recurring PDF footer with page numbers displayHeaderFooter and footerTemplate Puppeteer supplies PDF-layer footer rendering and page-number placeholders.
One element on only the final page Document structure or page-specific composition A fixed element is repeated on every page, so it cannot express last-page-only placement by itself.

These approaches solve different problems. Decide first whether the item is document content or page furniture, whether it should repeat, and whether it needs page-number metadata.

How A4 and print media work in Puppeteer

page.pdf() renders with the print CSS media type. Rules inside @media print therefore apply to the generated PDF, while screen-only styling does not. A DIN A4 sheet is 210 × 297 mm (8.2677 × 11.6929 inches).

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

You can select A4 in Puppeteer with format: 'A4', in CSS with @page { size: A4; }, or both. If CSS should control the paper dimensions, set preferCSSPageSize: true. Avoid accidental conflicts between CSS size, format, and explicit width/height values.

Recommended solution: a fixed element in print CSS

CSS for an A4 page and bottom element

@page {
  size: A4 portrait;
  /* Reserve printable space for the bottom element. */
  margin: 15mm 15mm 22mm;
}

@media print {
  .page-bottom {
    position: fixed;
    left: 15mm;
    right: 15mm;
    bottom: 5mm;
    height: 10mm;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 9pt;
  }
}

The 22 mm bottom page margin and the 5 mm offset are independent decisions. The margin protects the body’s flow; the offset places the fixed box inside the page. Adjust both to the element’s real height. If the element is taller than the reserved space, content can collide with it.

Complete runnable Puppeteer example

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();

    await page.setContent(`
      <!doctype html>
      <html>
      <head>
        <meta charset="utf-8">
        <style>
          @page {
            size: A4 portrait;
            margin: 15mm 15mm 22mm;
          }
          * { box-sizing: border-box; }
          body {
            margin: 0;
            font-family: Arial, sans-serif;
            color: #222;
          }
          .content { line-height: 1.45; }
          .page-bottom {
            position: fixed;
            left: 15mm;
            right: 15mm;
            bottom: 5mm;
            height: 10mm;
            display: flex;
            align-items: center;
            justify-content: center;
            border-top: 0.2mm solid #aaa;
            font-size: 9pt;
          }
          @media screen {
            .page-bottom { position: static; margin-top: 2rem; }
          }
        </style>
      </head>
      <body>
        <main class="content">
          <h1>A4 report</h1>
          <p>Your flowing content goes here. Add enough content to verify page breaks.</p>
        </main>
        <div class="page-bottom">Confidential report</div>
      </body>
      </html>
    `, { waitUntil: 'networkidle0' });

    await page.pdf({
      path: 'report.pdf',
      format: 'A4',
      printBackground: true,
      preferCSSPageSize: true
    });
  } finally {
    await browser.close();
  }
})();

Save this as make-pdf.js, install Puppeteer with npm install puppeteer, and run node make-pdf.js. The resulting report.pdf has the fixed element on every page.

Why the element repeats

In paged print output, CSS fixed positioning is page-relative. Chromium lays out the fixed box for each printed page, so a multi-page document receives one copy per page. This is useful for repeating labels, rules, logos, or legal text, but it is the wrong mechanism for a footer that must appear only once at the end.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Puppeteer’s PDF footer template for page numbers

When the content is genuinely a PDF footer, keep it out of the document flow and let Puppeteer render it. The special pageNumber and totalPages classes are replaced during PDF generation.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setContent('<h1>Report</h1><p>Document content...</p>');

    await page.pdf({
      path: 'report-with-footer.pdf',
      format: 'A4',
      displayHeaderFooter: true,
      footerTemplate: `
        <div style="width:100%; font-size:9px; text-align:center;">
          <span class="pageNumber"></span> /
          <span class="totalPages"></span>
        </div>
      `,
      margin: {
        top: '15mm',
        right: '15mm',
        bottom: '18mm',
        left: '15mm'
      }
    });
  } finally {
    await browser.close();
  }
})();

The bottom margin must be tall enough for the template. Tune it to the footer’s font, padding, and borders. Use one footer mechanism at a time unless you intentionally want both; combining a fixed footer and a template commonly produces duplicate material.

Handling a single-page or last-page-only element

“Bottom of the page” can mean the bottom of one A4 sheet or the bottom of the final sheet in a longer document. A fixed element cannot distinguish those cases: it repeats on every page. For a final-page-only signature, totals block, or approval line, put the element in a final section and control the document’s page breaks.

.final-block {
  break-before: page; /* use only when a new final page is intended */
  min-height: 250mm;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

Alternatively, generate the final page as a separate HTML document and combine PDFs in a later processing step. This makes the “once only” requirement explicit instead of relying on fixed positioning.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Margins, flow, and layout details that prevent overlap

Reserve vertical space

  • Set a larger bottom value in @page than the footer’s measured height.
  • Keep the fixed element’s bottom offset inside the page margin rather than placing it over body content.
  • Use box-sizing: border-box when borders and padding must fit a measured height.
  • Inspect long paragraphs, tables, and images at page boundaries; a footer can hide content even when the first page looks correct.

Control dimensions and orientation

Use size: A4 landscape for landscape output and match Puppeteer’s options accordingly. If you use preferCSSPageSize: true, let the CSS @page rule be authoritative. Do not mix an A4 CSS rule with unrelated explicit dimensions unless that priority is deliberate.

Backgrounds, fonts, and assets

Set printBackground: true when the element depends on background colors or images. Wait for web fonts and images before generating the PDF; otherwise the element’s measured height can change after pagination. For remote assets, ensure the page can actually access them from the rendering environment.

Debugging and troubleshooting

The element is missing from the PDF

  • Cause: the rule is screen-only or overridden by another selector. Fix: put the rule in @media print or unscoped print CSS and inspect computed styles before calling page.pdf().
  • Cause: the element is outside the loaded DOM. Fix: await page.setContent(), navigation, and any asynchronous rendering before printing.

The footer overlaps body text

Increase the bottom @page margin, reduce the fixed element’s height, or shorten the footer. A fixed box does not push normal flow downward, so ordinary content cannot reserve space for it automatically.

The footer appears on every page but was wanted only on the last page

This is expected fixed-position behavior. Replace it with a final document section, page-break logic, or separate-page composition.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Page numbers are blank

Verify that displayHeaderFooter: true is enabled and that the template uses the exact pageNumber and totalPages class names. Reserve bottom margin for the template.

CSS page size seems ignored

Set preferCSSPageSize: true when @page should take precedence. Remove conflicting width, height, or format settings while diagnosing.

Different Chromium versions produce different breaks

Pagination is sensitive to browser version, fonts, and resource timing. Pin Puppeteer in production, use the same Chromium build in CI and deployment, wait for fonts and images, and review generated PDFs after upgrades. The documented API behavior can change as browser versions change.

Performance and reliability practices

  • Reuse a browser process for batches of PDFs, but create a fresh page per document and close pages promptly.
  • Wait for the condition your page actually needs instead of using an unnecessarily long fixed delay.
  • For pages with lazy images, scroll or trigger the application’s loading behavior before printing, then wait for the images to finish.
  • Use deterministic fonts and asset URLs so line wrapping—and therefore page count—does not vary between machines.
  • Keep footer markup small. Complex scripts or network requests in a footer template can make output less predictable.
  • Open representative PDFs in an automated or manual review step to catch clipped text, overlaps, blank pages, and unexpected final-page placement.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a website screenshot and PDF API when you do not want to maintain Chromium, page CSS, and print orchestration yourself. Its PDF options include paper size, margins, landscape mode, page ranges, waits, custom CSS and JavaScript, and other capture controls. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing result in X-Page-Verdict and X-Billed headers. ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a direct capture, see the ScreenshotNeo documentation and call the API:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan. Create a free ScreenshotNeo account to try it.

Best Value
The SQL Programming Language: .
  • Used Book in Good Condition

FAQ

Does position: absolute place an element at the bottom of every PDF page?

No. Absolute positioning is relative to its containing block and normally places the element once in document layout. Use print-fixed positioning for a repeating page anchor or a PDF footer template for footer-layer content.

Can a fixed footer use percentage widths?

Yes, but test the result with your selected margins and orientation. Explicit left and right offsets in millimetres make the printable width easier to predict on A4.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Should I use format: 'A4' if my CSS already says A4?

You may specify both. Add preferCSSPageSize: true when the CSS @page dimensions should win, and avoid contradictory dimensions.

Frequently Asked Questions

Can a fixed-position element appear only on the final PDF page?

No. In paged print output, fixed content repeats on every page. Use a final document section or generate the last page separately.

Which option is better for page numbers?

Use Puppeteer’s PDF footer with displayHeaderFooter and footerTemplate, plus the pageNumber and totalPages placeholders.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.