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 →If a Puppeteer PDF breaks at different points on Heroku than on your computer, do not begin by adding arbitrary break-before rules. Make the rendering inputs identical first: print media, fonts, browser and Puppeteer versions, paper geometry, margins, scale, HTML data, and Linux dependencies. Then wait for the intended fonts, generate both PDFs from the same fixture, and locate the first page where the layouts diverge.
Why Heroku can paginate the same page differently
A PDF is produced by a browser layout engine, not by copying the screen. Small changes in available width or height can move a line, which changes every block below it and eventually shifts page breaks.
Puppeteer uses print CSS by default
page.pdf() generates the document with the print CSS media type. Rules inside @media print can change display, dimensions, colors, overflow, and visibility compared with the screen view. Heroku may therefore appear to “break” a page differently even when the HTML is identical.
If the PDF is intentionally supposed to match screen styling, select it explicitly before printing:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems#1 Best Overall
await page.emulateMediaType('screen');
const pdf = await page.pdf(pdfOptions);
Do not use screen media as a workaround for an accidental print stylesheet. Inspect and correct the print rules instead.
Fonts change line wrapping
A missing font is silently replaced by an available fallback. Different glyph widths alter line wrapping, element heights, and therefore pagination. This is especially visible with CJK and other non-Latin scripts. Puppeteer waits for document fonts by default, but waiting cannot install a font that is absent from the Heroku slug.
Browser and Linux environments are not identical
Different Chromium builds, Puppeteer versions, font libraries, or shared Linux libraries can produce different layout results. Heroku’s Puppeteer guidance notes that extra dependencies may be required, that requirements vary with the browser package, and that launching may require the --no-sandbox argument in that environment.
Paper geometry leaves less or more room
Puppeteer’s PDF defaults matter. format defaults to Letter, scale to 1, preferCSSPageSize to false, and margins are undefined (no margins set). A different paper size, margin, or scale changes the usable page area. A CSS @page size can also compete with API options.
First, create a reproducible comparison
- Save one representative HTML/data fixture, including the exact records, locale, timezone, and images used by the failing document.
- Generate a PDF locally and on Heroku with the same URL, input data, wait conditions, and PDF options. Download both files without post-processing.
- Record the package-lock entry,
puppeteer.version()result, actual Chromium/Chrome version, Heroku stack, buildpacks, environment variables, loaded fonts, print CSS, and completepage.pdf()options. - Compare the first page where content differs. The first divergence is more useful than counting the final number of pages: inspect the preceding element’s computed size, font, margin, and page-break properties.
- Reduce the fixture to the smallest HTML that still reproduces that first divergence. Change one variable at a time—fonts, geometry, print CSS, then browser stack.
This process distinguishes a rendering-input problem from a genuinely incorrect manual break rule.
Rank #2
Make PDF media and CSS page rules deliberate
Audit print-only rules
Search every stylesheet for @media print, @page, display:none, width declarations, overflow, and page-break properties. Check whether a production-only stylesheet, asset URL, or feature flag is changing the print tree.
@page {
size: A4;
margin: 14mm 12mm;
}
@media print {
.screen-only { display: none !important; }
.invoice { break-inside: avoid; }
}
Choose one source of page size
Use API geometry when the application owns the PDF contract:
const pdfOptions = {
format: 'A4',
margin: {
top: '14mm',
right: '12mm',
bottom: '14mm',
left: '12mm'
},
preferCSSPageSize: false,
scale: 1,
printBackground: true,
waitForFonts: true
};
Use CSS geometry when designers own it, and allow it to win:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
const pdfOptions = {
preferCSSPageSize: true,
printBackground: true,
scale: 1,
waitForFonts: true
};
When format is supplied, it takes priority over width and height. With preferCSSPageSize: true, CSS @page size takes priority instead. Do not mix competing definitions casually. Keep scale identical in both environments; its documented range is 0.1–2, with 1 as the default.
Use a deterministic Puppeteer PDF routine
The following example makes the important choices explicit. Adapt the URL, selector, and PDF options to your application.
const puppeteer = require('puppeteer');
async function createPdf(url) {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
try {
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 900, deviceScaleFactor: 1 });
await page.goto(url, { waitUntil: 'networkidle0', timeout: 90000 });
// Keep this enabled unless you have a documented reason not to.
await page.evaluate(async () => {
await document.fonts.ready;
});
await page.waitForSelector('[data-pdf-ready]', { timeout: 30000 });
// page.pdf() uses print media by default. Select screen only deliberately.
// await page.emulateMediaType('screen');
const pdf = await page.pdf({
format: 'A4',
margin: { top: '14mm', right: '12mm', bottom: '14mm', left: '12mm' },
preferCSSPageSize: false,
scale: 1,
printBackground: true,
waitForFonts: true,
path: 'output.pdf'
});
return pdf;
} finally {
await browser.close();
}
}
createPdf(process.argv[2]).catch(error => {
console.error(error);
process.exitCode = 1;
});
waitForFonts is true by default in Puppeteer’s PDF API. Keeping it explicit makes reviews easier, but it does not solve missing font files or a failed font request. The readiness marker in this example should be set by your application only after data and critical assets are present.
Make fonts identical on Heroku
- List the font families and exact files used by the document, including weight and style variants. A regular face substituted for a semibold face can change wrapping.
- Ship the required font files with the application or install them through the deployment method supported by your current Heroku stack and browser package.
- Use stable, local
@font-faceURLs and verify that requests return successfully in the deployed app. - Wait for
document.fonts.ready(and keep Puppeteer’swaitForFontsenabled) before callingpage.pdf(). - For CJK documents, check every required character range and fallback family; a partially available family can create mixed metrics.
To diagnose rather than guess, log loaded faces in the page and compare computed styles for a representative text node:
const fontReport = await page.evaluate(() => ({
ready: document.fonts.status,
faces: [...document.fonts].map(face => ({
family: face.family,
weight: face.weight,
style: face.style,
status: face.status
})),
sample: getComputedStyle(document.querySelector('[data-font-sample]'))?.font
}));
console.log(fontReport);
Align Heroku’s browser runtime
Pin what you can compare
Keep the Puppeteer dependency and lockfile stable while diagnosing. Capture the actual browser version from each environment rather than assuming that a package version implies a particular Chromium build. Also record the Heroku stack and every buildpack involved.
Install compatible libraries and buildpack support
The current Puppeteer Heroku troubleshooting guidance directs users to add the Puppeteer Heroku buildpack through the app’s buildpack settings and to verify required Linux browser libraries. Because required libraries vary, inspect the deployed binary rather than copying an old list:
ldd /path/to/chrome | grep not
Any missing shared library must be addressed using configuration compatible with your current stack and browser package. A browser that starts locally but fails, falls back, or exits early on Heroku is not a pagination problem.
Use sandbox flags only as required by the deployment
Heroku guidance describes launching with --no-sandbox. Apply that setting only to the Heroku launch configuration where it is required, and keep the browser and dependency versions pinned during comparison.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Common symptoms and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Every page breaks earlier on Heroku | Smaller usable area from paper size, margins, scale, or a different font | Log all PDF options, computed print dimensions, and loaded fonts; set geometry explicitly. |
| Only headings or long paragraphs move | Font substitution or a different weight/style face | Ship the exact faces, verify their network requests and status, then wait for fonts. |
| Screen looks correct but PDF differs | Print media rules | Inspect @media print; use emulateMediaType('screen') only if screen styling is the intended PDF design. |
| PDF is blank or times out on Heroku | Browser dependency failure, navigation timeout, blocked asset, or bot check | Inspect browser logs, network failures, buildpack/libraries, and readiness selectors before investigating breaks. |
| CSS page size appears ignored | preferCSSPageSize is false or API format wins |
Set preferCSSPageSize: true when CSS should control size, or remove competing CSS and set API dimensions. |
| Manual breaks work locally but not in production | Underlying layout inputs differ | Align fonts, geometry, media, and browser versions first; only then tune break-before, break-after, or break-inside. |
Validate the fix and keep it stable
Compare layout, not just page count
A matching page count can hide a one-line shift that breaks a signature block later. Compare the first divergent page, element bounding boxes, computed font values, and the PDF’s paper dimensions. Store a known fixture and regenerate it after Puppeteer, browser, font, Heroku stack, or print-CSS changes.
Change one variable at a time
- First align print versus screen media.
- Then align font files, weights, and readiness.
- Then align paper size, margins, CSS precedence, and scale.
- Finally align browser/Puppeteer versions and Linux dependencies.
Only after those inputs match should you adjust manual page-break rules. A break rule cannot reliably compensate for changed glyph metrics or a different printable area.
Performance and reliability considerations
networkidle0 can wait indefinitely on applications with analytics or streaming requests; use a reliable application readiness selector and a bounded timeout. Loading every lazy image for a very long document increases memory and generation time, so test representative maximum-length inputs. Close the browser in a finally block, and capture browser console, request-failure, and process-exit logs in production.
Or skip the browser setup
If your goal is a clean screenshot or PDF of a URL rather than controlling a Heroku browser process, ScreenshotNeo provides a single HTTP endpoint. Its cleanup step accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks or 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. It also offers an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo API documentation for output and option details. Equivalent calls are:
Best Value
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}`);
There is no browser setup to maintain, and the free plan includes 1,000 screenshots a month without a card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Should I force a page break after every section?
No. Forced breaks hide the cause and become unstable when content, fonts, or paper dimensions change. Use them only after the rendering inputs are aligned and a deliberate document-design rule requires them.
Does increasing the viewport fix PDF pagination?
Usually not. Viewport settings affect responsive layout, while PDF paper size, margins, scale, and print CSS determine the printed page. Set both deliberately when responsive breakpoints are part of the design.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC 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 & 11Can a cache explain different breaks?
Yes, if one environment receives different HTML, CSS, font, or image bytes. Log asset URLs and response status, use the same fixture, and disable application-level content variation while diagnosing.
What evidence should I provide when escalating the issue?
Provide both PDFs, the smallest reproducible HTML/data fixture, exact PDF options, print CSS, loaded-font report, Puppeteer and browser versions, Heroku stack/buildpacks, and the first page where output diverges.
The Bottom Line
Consistent Heroku PDFs come from identical rendering inputs—not from piling on page-break declarations. Pin the browser stack, provide the same fonts, make print geometry explicit, wait for real readiness, and compare a reproducible fixture before changing layout rules.
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.




