Recommended Free Tools
Use CSS paged-media counters in the PDF renderer, not ordinary text in the document. Add counter(page) to an @page margin box, leave enough page margin for the footer, and verify that your rendering engine supports the required counters. For a total such as “Page 3 of 12,” use counter(pages) only when that engine documents support for it.
The basic CSS pattern
Put the page number in a page-margin box. The following works in engines that implement generated content in paged-media margins:
| # | Preview | Product | Price | |
|---|---|---|---|---|
| 1 |
|
Aviation Instructor's Handbook: FAA-H-8083-9B | $15.99 | Buy on Amazon |
| 2 |
|
Handbook of Attachment: Theory, Research, and Clinical Applications | $84.98 | Buy on Amazon |
| 3 |
|
Wilderness First Aid Handbook | $16.99 | Buy on Amazon |
| 4 |
|
The Tarot Handbook: Practical Applications of Ancient Visual Symbols | $18.14 | Buy on Amazon |
@page {
margin: 18mm 16mm;
@bottom-right {
content: counter(page);
}
}
The page counter is the current page number. The footer is outside the document’s normal flow, so it does not require an extra element in your HTML. The bottom margin must be large enough for the margin box; otherwise the number can overlap the last line of content.
Show the total number of pages
@page {
margin: 18mm 16mm;
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
}
}
counter(pages) requires a renderer that calculates and exposes the final page count. Chrome documents both counters for print margins, and Prince documents the same pattern. Do not assume that an arbitrary HTML-to-PDF converter supports the total counter just because it supports counter(page).
#1 Best Overall
Put the number on the left or center
@page {
margin: 18mm 16mm;
@bottom-left {
content: counter(page);
}
@bottom-center {
content: "Page " counter(page) " of " counter(pages);
}
}
Use one margin box for one footer. If you use several boxes, inspect the PDF for collisions with logos, dates, or automatically generated browser text.
Make the CSS apply to print output
Place the rule in your print stylesheet or in a <style> element that is present when the renderer loads the page. A complete minimal document is:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Invoice</title>
<style>
@media print {
@page {
size: A4;
margin: 18mm 16mm 22mm;
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
font-size: 9pt;
color: #555;
}
}
}
h1 { break-after: avoid; }
table { break-inside: avoid; }
</style>
</head>
<body>
<h1>Invoice</h1>
<p>Content that spans several printed pages goes here.</p>
</body>
</html>
If the renderer does not support margin boxes, a footer element in normal HTML flow is not a reliable substitute: it will repeat only when the engine has a dedicated repeating-header/footer feature, and it can change the document’s pagination. Prefer a renderer with documented paged-media support.
Check the renderer and version before relying on the rule
| Renderer or workflow | What is documented | What to verify |
|---|---|---|
| Chrome print output | Chrome documents generated content in page margins from Chrome 131, including the page and pages counters. |
The installed Chrome version, margin-box behavior, total-counter behavior, and print-dialog header/footer settings. |
| Puppeteer | Page.pdf() creates a PDF using print CSS by default. Puppeteer also documents emulating screen media when screen styles are required. |
The Chromium version bundled or selected by your installation, whether you need print or screen media, and whether browser headers and footers are disabled. |
| Prince | Prince documents page-margin boxes, counter(page), counter(pages), page selectors, and first-page overrides. |
Your Prince version and the exact page selectors and layout features used by your document. |
| WeasyPrint | Its documentation lists page-margin boxes, page-based counters, page selectors, and known limitations. | The installed release and whether its documented limitations affect your CSS. |
CSS paged-media support is not uniform across browsers and dedicated converters. Test the exact engine, version, fonts, paper size, and input document that you deploy.
Chrome and Puppeteer: a complete scripted example
Install Puppeteer in a new Node.js project with npm install puppeteer. Save your HTML as invoice.html, then run:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('file:///absolute/path/to/invoice.html', {
waitUntil: 'networkidle0'
});
await page.pdf({
path: 'invoice.pdf',
format: 'A4',
printBackground: true,
displayHeaderFooter: false,
preferCSSPageSize: true,
margin: { top: '18mm', right: '16mm', bottom: '22mm', left: '16mm' }
});
} finally {
await browser.close();
}
})();
Page.pdf() uses print CSS by default, so the @page rule is active. Set displayHeaderFooter: false so Chromium’s automatic header/footer does not duplicate your authored margin content. If your page is designed with screen media instead, call await page.emulateMediaType('screen') before generating the PDF and confirm that the resulting pagination still fits.
For a URL rather than a local file, replace the file: URL with an HTTPS URL. Wait for the content that determines layout—fonts, images, charts, or API data—before calling page.pdf(); otherwise the page count can change after the PDF is produced.
First pages, facing pages, and book-style layouts
Some engines expose page selectors in addition to the base @page rule. Prince documents these patterns:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11@page:first {
@bottom-right { content: none; }
}
@page:left {
@bottom-left { content: counter(page); }
}
@page:right {
@bottom-right { content: counter(page); }
}
Use @page:first when a title page should be unnumbered, and :left/:right when odd and even pages need mirrored footer placement. These selectors are engine-dependent; confirm support before shipping them in Chrome or WeasyPrint.
Browser print-dialog settings that can create duplicate numbers
- Open the browser’s print dialog or set the equivalent automation option.
- Turn off automatic headers and footers. Browsers may add their own URL, title, date, or page number when there is room, even when your CSS supplies a margin-box footer.
- Keep the authored bottom margin large enough for the number and any label.
- Generate the PDF and inspect the first, middle, and last pages—not just the preview.
Do not judge only by a screen screenshot. PDF pagination can differ because print styles, font loading, paper size, and margin settings alter line wrapping.
Rank #3
- Quality material used to make all Pro force products
- Tested in the field and used in the toughest environments
- 100 percent designed in the USA
- The Wilderness First Aid Handbook is a must-have for every back pocket or backpack
- Filled with original, full-color artwork illustrating the techniques and procedures described and with internal-spiral binding and waterproof pages
“Page X of Y” accuracy and asynchronous content
The final page count is known only after layout completes. Dynamic applications should wait for their data and fonts before printing. In Puppeteer, networkidle0 helps with network activity, but it is not a guarantee that JavaScript has finished rendering. Add an application-specific readiness flag or wait for a selector that appears only when the page is complete.
If the total is blank, shows an unexpected value, or is rejected by the engine, fall back to the current-page counter or use a renderer that documents counter(pages). Never label a PDF “Page X of Y” until the target runtime has produced and verified that value.
Common failures and fixes
No number appears
- Confirm that the rule is present in the printed document and not excluded by a print stylesheet condition.
- Check the engine and version. Chrome’s documented generated-margin-content support starts with Chrome 131.
- Verify that the syntax is an
@pagemargin box such as@bottom-right, not an ordinary selector insidebody.
The footer overlaps the content
- Increase the bottom value in
@page { margin: ... }. - Reduce footer font size or content length.
- Regenerate after changing paper size; A4, Letter, and custom sizes have different usable heights.
Two sets of page numbers appear
- Disable browser-added headers and footers in the print dialog or automation configuration.
- Search your template for a second footer, PDF post-processing step, or JavaScript-generated page label.
The total page count is missing or wrong
- Check whether the selected engine supports
counter(pages); support differs by product and version. - Wait for images, fonts, and application data before printing.
- Confirm that the PDF is not being modified after the renderer calculates its counters.
The title page should not be numbered
Use the engine’s page-selector support, such as Prince’s documented @page:first override. If your engine does not implement that selector, create a separate unnumbered title-page document or use a renderer with the required paged-media feature.
The PDF looks different from the web page
Puppeteer prints with print media by default. If the design depends on screen styles, explicitly emulate screen media before printing, then retest page breaks and margins. Also wait for web fonts and late-loading images.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
ScreenshotNeo can capture a URL as a clean screenshot or PDF through one GET request. It accepts the consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. For PDF output, use the PDF options documented in the ScreenshotNeo documentation.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/invoice -o shot.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/invoice"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/invoice' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Beyond PDF capture, ScreenshotNeo provides full-page capture with lazy images loaded, custom CSS and JavaScript, selector waits or delays, network-idle waits, cookies and headers, viewport and device presets, paper size, margins, landscape mode, page ranges, async jobs with signed webhooks, bulk capture for up to 100 URLs per call, caching with a chosen TTL, and an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
Rank #4
- Used Book in Good Condition
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is on every plan. Create a free ScreenshotNeo account.
Production checklist
- Choose a renderer and pin its version.
- Confirm support for margin boxes and, if needed,
counter(pages). - Set a bottom page margin that reserves footer space.
- Disable automatic browser headers and footers.
- Wait for data, fonts, images, and layout-changing scripts.
- Test first, middle, last, title, and facing pages at every supported paper size.
- Inspect the actual PDF for overlap, duplicate furniture, missing totals, and clipped content.
Frequently Asked Questions
Can I add page numbers with only an HTML footer element?
Not reliably across HTML-to-PDF engines. A normal footer follows document flow; use a renderer’s paged-media margin boxes or its documented repeating-footer feature.
Why does Chrome show a page number in the preview but not in my deployed PDF?
The deployed runtime may use an older Chrome version or a different converter. Generated content in page margins is documented for Chrome starting with version 131, so verify the actual runtime.
Should I use `counter(pages)` for legal documents without testing?
No. Total-page support is engine- and version-dependent. Generate a sample with the production runtime and verify the final value before relying on it.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Does changing paper size affect page numbering?
Yes. Paper size, margins, fonts, and late-loading content can change pagination, so test each output format you publish.
Quick Recap
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.

