Free tools Windows power users keep installed
One-click scans. No signup required.
To leave a cover page unnumbered and show “1” on physical page two, generate the cover and numbered content as separate PDFs, then merge them. Puppeteer’s page.pdf() can add a footer with pageNumber and totalPages, but its documented template API does not provide a reliable “skip only the first page” switch. If physical page two should display “2”, generate the entire document once with the footer enabled.
First decide what “start on the second page” means
There are two legitimate numbering conventions:
| Physical PDF page | Displayed number | Use this when |
|---|---|---|
| Cover (page 1) | No number | The cover is outside the report’s numbered section. |
| First content page (page 2) | 1 | The report, thesis, or invoice body has its own sequence. |
| Cover (page 1) | No number | The cover is decorative, but the document keeps one physical sequence. |
| First content page (page 2) | 2 | Readers need numbers to match the PDF’s physical page order. |
Do not change the numeral with CSS until you have chosen the convention. Puppeteer’s built-in pageNumber follows the physical PDF sequence, so a one-pass document naturally displays 2 on physical page two.
How Puppeteer PDF footers work
Puppeteer generates PDFs using the print CSS media type. Set displayHeaderFooter: true and provide a footerTemplate (or headerTemplate). The default for displayHeaderFooter is false, so a footer template alone does not turn numbering on.
Inside a header or footer template, Chromium replaces <span class="pageNumber"></span> with the current page and <span class="totalPages"></span> with the total page count. These placeholders are injected by the PDF header/footer mechanism; ordinary body HTML does not receive those values.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
- EDIT text, images & designs in PDF documents. ORGANIZE PDFs. Convert PDFs to Word, Excel & ePub.
- READ and Comment PDFs – Intuitive reading modes & document commenting and mark up.
- CREATE, COMBINE, SCAN and COMPRESS PDFs
- FILL forms & Digitally Sign PDFs. PROTECT and Encrypt PDFs
- LIFETIME License for 1 Windows PC or Laptop. 5GB MobiDrive Cloud Storage Included.
Option A: page two displays 2 (single PDF)
Use this when the physical sequence is the authoritative sequence. Keep the cover in the same HTML document and enable the footer for every page.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setContent(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
@page { size: A4; margin: 22mm 18mm 22mm; }
* { box-sizing: border-box; }
body { font-family: Arial, sans-serif; margin: 0; }
.cover { height: 245mm; display: grid; place-items: center; page-break-after: always; }
h1 { font-size: 32px; }
.content { page-break-before: always; }
</style>
</head>
<body>
<section class="cover"><h1>Report title</h1></section>
<main class="content">
<h2>Introduction</h2>
<p>Your numbered content starts here.</p>
</main>
</body>
</html>`, { waitUntil: 'networkidle0' });
await page.pdf({
path: 'report.pdf',
format: 'A4',
displayHeaderFooter: true,
headerTemplate: '<div></div>',
footerTemplate: `
<div style="width:100%; font-size:9px; text-align:center;">
<span class="pageNumber"></span> / <span class="totalPages"></span>
</div>`,
printBackground: true,
margin: { top: '18mm', bottom: '18mm', left: '18mm', right: '18mm' }
});
} finally {
await browser.close();
}
})();
Because the footer is enabled for the complete document, the first physical page is 1 and the first content page is 2. Header and footer margins must be large enough to prevent the footer from colliding with body text.
Option B: page two displays 1 (cover excluded)
Render two PDFs. The cover has no header or footer. The body starts its own PDF sequence at 1. Merge the files in order. This is a composition of Puppeteer’s documented controls and a PDF-merging library, not a special page.pdf() mode.
1. Render the unnumbered cover
await page.setContent(coverHtml, { waitUntil: 'networkidle0' });
await page.pdf({
path: 'cover.pdf',
format: 'A4',
displayHeaderFooter: false,
printBackground: true
});
2. Render the numbered body
await page.setContent(bodyHtml, { waitUntil: 'networkidle0' });
await page.pdf({
path: 'body.pdf',
format: 'A4',
displayHeaderFooter: true,
headerTemplate: '<div></div>',
footerTemplate: `
<div style="width:100%; font-size:9px; text-align:center;">
<span class="pageNumber"></span> / <span class="totalPages"></span>
</div>`,
printBackground: true,
margin: { bottom: '18mm' }
});
The first page of body.pdf is numbered 1. After it is placed behind cover.pdf, that page becomes physical page two in the final file but still displays 1.
3. Merge the PDFs
One practical choice is pdf-lib. Install it with npm install pdf-lib, then merge the cover before the body:
const fs = require('node:fs/promises');
const { PDFDocument } = require('pdf-lib');
async function mergePdfFiles(output, ...files) {
const merged = await PDFDocument.create();
for (const file of files) {
const source = await PDFDocument.load(await fs.readFile(file));
const pages = await merged.copyPages(source, source.getPageIndices());
pages.forEach(page => merged.addPage(page));
}
await fs.writeFile(output, await merged.save());
}
mergePdfFiles('report.pdf', 'cover.pdf', 'body.pdf')
.catch(error => { console.error(error); process.exitCode = 1; });
Keep the same paper size, orientation, fonts, and intended margins in both renders. Otherwise the merged file can show a visible jump between the cover and body.
A complete split-and-merge script
The following script demonstrates the whole workflow. Replace the sample HTML with your templates or generated content.
const puppeteer = require('puppeteer');
const fs = require('node:fs/promises');
const { PDFDocument } = require('pdf-lib');
const footerTemplate = `
<div style="width:100%; font-size:9px; text-align:center;">
<span class="pageNumber"></span> / <span class="totalPages"></span>
</div>`;
async function merge(output, inputs) {
const result = await PDFDocument.create();
for (const input of inputs) {
const source = await PDFDocument.load(await fs.readFile(input));
const pages = await result.copyPages(source, source.getPageIndices());
pages.forEach(page => result.addPage(page));
}
await fs.writeFile(output, await result.save());
}
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const common = `<style>@page{size:A4;margin:22mm 18mm}body{font-family:Arial}</style>`;
const coverHtml = `<html><head>${common}</head><body><div style="height:245mm;display:grid;place-items:center"><h1>Cover</h1></div></body></html>`;
const bodyHtml = `<html><head>${common}</head><body><h1>Chapter 1</h1><p>Body content.</p></body></html>`;
await page.setContent(coverHtml, { waitUntil: 'networkidle0' });
await page.pdf({ path: 'cover.pdf', format: 'A4', displayHeaderFooter: false, printBackground: true });
await page.setContent(bodyHtml, { waitUntil: 'networkidle0' });
await page.pdf({
path: 'body.pdf', format: 'A4', displayHeaderFooter: true,
headerTemplate: '<div></div>', footerTemplate,
printBackground: true, margin: { bottom: '18mm' }
});
await merge('report.pdf', ['cover.pdf', 'body.pdf']);
} finally {
await browser.close();
}
})();
Why CSS-only fixes are unreliable
Paged-media CSS defines counter(page) and counter(pages), and Chrome documents those counters for print margin content. Puppeteer’s header/footer template path is separate from ordinary document CSS. In addition, compatibility guidance for Puppeteer reports that @page :first is unsupported. A production workflow should therefore not depend on @page :first to hide only the first footer.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsRank #2
- Edit PDFs with Ease. Modify text, images, and layouts directly within your PDF documents.
- Convert & Organize. Export PDFs to Word, Excel, or ePub, and organize files with ease.
- Read & Annotate. Enjoy intuitive reading modes and powerful tools to comment, highlight, and mark up PDFs.
- Create & Manage PDFs. Create new PDFs, combine multiple files, scan documents, and compress for easy sharing.
- Fill & Sign Forms. Complete forms and digitally sign documents with secure e-signature tools.
If you use CSS counters for other print styling, verify the result with the exact Chromium version bundled by your deployed Puppeteer release. Do not assume a browser preview and generated PDF paginate identically.
Tables of contents and total-page counts
In the split method, totalPages in the body footer is the number of body pages, not the final merged document’s total. That is usually correct when the body is a separately numbered section. If you need a single table of contents whose entries and total count include the cover, render the complete document in one pass or run a separate post-processing step that rewrites page labels and links.
Changing fonts, waiting for web fonts, image loading, margins, or content length can change page breaks. Generate the final body before relying on its total-page value.
Reliability and performance checklist
- Pin the Puppeteer package (and therefore its Chromium revision) in deployments.
- Use
waitUntil: 'networkidle0'only when the page can become idle; applications with long-lived connections may require an explicit readiness selector instead. - Wait for web fonts and critical images before calling
page.pdf(); otherwise late layout shifts can move content to another page. - Set explicit paper size, margins, orientation, and
printBackgroundso local and CI output match. - Close each browser and page in a
finallyblock to avoid leaked Chromium processes. - For repeated jobs, reuse a browser process but create a fresh page per document and cap concurrency according to available memory.
- Inspect the merged PDF, not only the intermediate files, and test a short document, a multi-page document, and a document whose final line lands near a page boundary.
Troubleshooting
The footer is missing
Confirm displayHeaderFooter: true. A footer template is ignored when that flag is false. Also check that your template contains the documented pageNumber span and that the bottom margin leaves room for it.
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 & 11Page two shows 2 instead of 1
You rendered one PDF, so Chromium is numbering physical pages. Render the cover and body separately, then merge them if the body must begin at 1.
The footer overlaps text
Increase margin.bottom and keep the footer’s CSS small. The margin in the PDF options, rather than padding inside the footer template, reserves printable space.
The cover unexpectedly has a number
Ensure the cover render sets displayHeaderFooter: false. In the split workflow, do not reuse a page configured for the body without explicitly changing the option.
Total pages are wrong
Check whether you are reading the body total after merging. Body totals exclude the cover by design. Also wait for fonts, images, and other layout-affecting resources before rendering.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Rank #3
- EVERY PDF TOOL UNLOCKED - 30+ tools in one app: edit text and images, convert, merge, split, compress, sign, OCR, redact, watermark, batch process, and more. No feature gates, no upsells, nothing held back.
- PAY ONCE, OWN FOREVER — A one-time purchase, not a subscription. Other apps runs $240/year — Scrivar is yours for life, with free updates included.
- UNLIMITED eSIGN, BUILT IN — Send contracts and forms for signature and track every step. Recipients sign in their browser with no account or app needed. Replace DocuSign and save hundreds a year.
- PC, MAC, AND WEB — Install on any Win 10/11 PC or macOS 11+ Mac (Intel or Apple Silicon), or work in your browser at scrivar.com. Same tools, same account, everywhere you work.
- OCR + FULL OFFICE CONVERSION — Turn scanned documents into searchable, selectable text, and convert PDFs to and from Word, Excel, and PowerPoint with formatting kept intact.
Blank pages appear
Look for an explicit page-break-before combined with a preceding page-break-after, oversized fixed-height elements, or margins that leave no usable page area. Test without forced breaks, then add only the break that is required.
The merged PDF has inconsistent sizing
Use the same format or explicit width and height for both PDFs. Mixing A4, Letter, portrait, or landscape settings produces visibly different pages even when the merge succeeds.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is simply to capture a finished web page as an image or PDF rather than control Puppeteer pagination, ScreenshotNeo provides a single HTTP request. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
See the complete parameter list in the ScreenshotNeo documentation. Example cURL request:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
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)
Node.js:
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 shots each month with no card. Paid plans start at $5 for 3,000 shots, and every feature is included on every plan. Create a free ScreenshotNeo account.
Practical decision guide
- Choose one-pass Puppeteer when page two should display 2 and you need one document-wide total.
- Choose split-and-merge when the cover is unnumbered and page two must display 1.
- Choose a post-processing workflow when a single merged total, cross-document links, or custom PDF page labels matter more than a simple footer.
Frequently Asked Questions
Can I set the first visible body number with a Puppeteer option?
No documented PDF option sets an arbitrary starting offset. The dependable approaches are physical numbering in one render or a separately rendered body merged after an unnumbered cover.
Does hiding a footer remove the page from the total?
No. A hidden or absent footer does not alter pagination; it only changes what is drawn in the header or footer area.
Should I merge PDFs inside Puppeteer?
Puppeteer creates each PDF but does not provide a built-in merge operation. Use a PDF library such as pdf-lib or an equivalent tool after rendering.
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.

