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

Short answer: wkhtmltopdf’s --margin-top, --margin-bottom, --margin-left, and --margin-right options apply to the entire PDF. They cannot begin at page two. For a single-file workaround, render a dedicated first-page wrapper, force a page break, and add padding inside the remaining content. If the second page must have a genuinely different physical page box, render the first page and body as separate PDFs with different margins, then merge them.

What wkhtmltopdf can and cannot change

The command-line margin options are document-wide settings. For example:

wkhtmltopdf --margin-top 10mm --margin-bottom 15mm --margin-left 15mm --margin-right 15mm input.html output.pdf

Those values are passed to the print system before pagination. There is no documented page-range form such as “use 40 mm on page one and 15 mm from page two.” Supplying --margin-top twice does not create a page-specific rule; the last value simply wins for the document.

CSS has paged-media constructs such as @page, @page :first, @page :left, and @page :right. They are valid CSS concepts, but wkhtmltopdf uses an older Qt/WebKit pagination pipeline. Its manual describes laying out a long page and cutting it into sheets, and support for page-specific margins is incomplete and build-dependent. Treat @page :first as an experiment, not a reliable production solution.

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

Choose the right meaning of “different margin”

Before changing HTML, decide whether you need to move content or change the printable page area.

Requirement Best method What changes Important trade-off
More blank space above a cover or letterhead One PDF, first-page wrapper plus padding on body pages Content position inside the same page box Physical margins remain global
Different left/right/top/bottom page box on page one Two renders, then merge PDFs Actual printable area per render Post-processing may require checking links and outlines
Different margins on alternating pages Separate rendering or a renderer with dependable paged-media support True per-page geometry Not dependable in stock wkhtmltopdf
Only a visual offset in one section Inner wrapper, padding, or positioned element Layout of that section Can affect wrapping and page count

Single-PDF workaround: first-page wrapper and forced break

This is the simplest approach when the first page is a cover, invoice header, or title sheet and the later pages merely need an inset. Set the CLI margins for the regular pages, keep the first page in its own block, and start the body explicitly on a new page.

Complete HTML example

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>
    @page { size: A4; }
    html, body { margin: 0; padding: 0; }
    body { font-family: Arial, sans-serif; font-size: 11pt; }

    /* The CLI margins are the real document-wide page margins. */
    .first-page {
      page-break-after: always;
      min-height: 240mm; /* tune for A4 and your CLI margins */
      box-sizing: border-box;
    }

    .first-page h1 { margin: 0; padding-top: 8mm; }
    .body-pages {
      page-break-before: always;
      padding-top: 20mm; /* simulated extra inset from page two onward */
    }

    .body-pages h2 { page-break-after: avoid; }
    table, img { page-break-inside: avoid; }
  </style>
</head>
<body>
  <section class="first-page">
    <h1>Quarterly report</h1>
    <p>Prepared for the customer</p>
  </section>

  <main class="body-pages">
    <h2>1. Summary</h2>
    <p>The regular report starts here, with a 20 mm content inset.</p>
    <h2>2. Detail</h2>
    <p>Additional pages flow normally.</p>
  </main>
</body>
</html>

Render it with the margins intended for the body pages:

wkhtmltopdf --page-size A4 --margin-top 12mm --margin-bottom 15mm --margin-left 15mm --margin-right 15mm report.html report.pdf

page-break-after on .first-page and page-break-before on .body-pages are deliberately redundant. Keeping the break on the boundary makes the intended transition clear and helps when one rule is affected by surrounding layout. The break must not be inside a floated parent. A reported wkhtmltopdf 0.12.x issue shows both properties being ignored when the parent is floated; removing the float allowed the break.

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

Why padding is safer than a first-child margin

Do not rely on margin-top on the first visible element of the body. wkhtmltopdf has a reported document-start bug in which that margin can be ignored. Put the inset on .body-pages with padding, or add an inner wrapper:

.body-pages { page-break-before: always; }
.body-pages-inner { padding-top: 20mm; }

Padding moves content while preserving the global page box. It can reduce the usable height, so headings, tables, and images may reflow onto additional pages. Tune min-height for the selected paper size and global margins rather than assuming a fixed pixel value.

When you need true physical margins: render and merge

If a cover must have a different printable area—not merely more blank space—use two wkhtmltopdf jobs. The first job receives the cover margins; the second receives the body margins. Then merge the resulting PDFs with a PDF tool available in your environment.

Render the two parts

wkhtmltopdf 
  --page-size A4 
  --margin-top 35mm --margin-bottom 20mm 
  --margin-left 25mm --margin-right 25mm 
  cover.html cover.pdf

wkhtmltopdf 
  --page-size A4 
  --margin-top 12mm --margin-bottom 15mm 
  --margin-left 15mm --margin-right 15mm 
  body.html body.pdf

Ensure both commands use the same paper size, orientation, zoom, DPI assumptions, fonts, and resource-loading options. A mismatch can produce a visibly different scale or page size after merging.

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.

Merge and validate

Use your approved PDF merger to append body.pdf to cover.pdf. The exact command varies by platform and merger, so verify the tool’s syntax rather than copying an untested command. After merging, inspect:

  • Page size and orientation of every page.
  • Hyperlinks, bookmarks, and table-of-contents outlines.
  • Font embedding and selectable text.
  • Headers, footers, images, and page numbers at the join.
  • Whether metadata or document-level outlines were retained.

The separate-render method is the dependable way to change the actual page box, but post-processing can affect links or outlines. Preserve source PDFs until the merged file has passed those checks.

Why @page :first often disappoints

CSS 2.2’s paged-media model allows a first-page rule such as:

@page { margin: 2cm; }
@page :first { margin-top: 10cm; }

That syntax expresses the requirement correctly in a standards-aware paged renderer. wkhtmltopdf’s WebKit engine does not consistently implement page selectors and page-box changes. A rule may be ignored, partially applied, or behave differently between binary builds. If you test it, test the exact wkhtmltopdf executable used in production and compare generated PDFs, but keep the wrapper or two-render method as your fallback.

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

Debugging page breaks and unexpected margins

The body starts on page one

  • Confirm that .first-page is not inside a floated container.
  • Inspect the generated PDF for oversized content that overflows the first-page block.
  • Keep both break rules on the non-floated boundary.
  • Try a minimal HTML file to exclude invalid nesting or CSS collisions.

The second-page inset is missing

  • Use padding on .body-pages or an inner wrapper, not only a first-child top margin.
  • Check for a later rule that sets padding-top: 0.
  • Remember that CLI margins are still global; CSS padding is only a content offset.

The break is ignored after adding a layout wrapper

Floats are the most important known trigger. Remove the float from the parent of the break, replace it with normal flow, or move the first-page/body boundary outside the floated element. Also remove absolute positioning around the boundary while testing.

Content overlaps or an extra blank page appears

  • Do not combine an oversized fixed height with large padding unless the box uses box-sizing: border-box.
  • Reduce min-height until the cover fits inside one page.
  • Use only one intentional page boundary after the minimal test; duplicate breaks can expose blank pages when an element already begins on a new page.
  • Check images and long tables, which can force overflow before the break is reached.

Margins differ between machines

wkhtmltopdf’s repository was archived on January 2, 2023, and pagination behavior varies by the exact 0.12.x binary, patched-Qt build, operating system, fonts, and command-line flags. Record the binary version, paper size, orientation, zoom, and installed fonts. Render a regression fixture in the same container or VM used in production.

Performance, reliability, and maintainability

Keep the single-render path deterministic

Use explicit paper size and margins, load the same fonts in every environment, and avoid layout that depends on viewport width. Give images fixed dimensions where possible. A small HTML fixture containing a cover, a forced break, a long paragraph, a table, and an image will reveal most pagination regressions.

Prefer two renders for contractual geometry

Invoices, forms, and preprinted stationery often require a real printable-area change. Separate PDFs make that requirement explicit and easier to test than relying on undocumented CSS behavior. The cost is an extra render and a merge-validation step.

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

Expect page-count changes

Increasing body padding reduces available height. Text wrapping, table splitting, and image placement can move content to later pages. Do not hard-code page numbers until the final binary, fonts, and assets are fixed.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is simply to obtain clean page images or PDFs from a URL rather than maintain a wkhtmltopdf pipeline, ScreenshotNeo provides a GET-based screenshot API. It accepts cookie and consent banners before capture, removes more than 60 known consent platforms plus newsletter popups and chat widgets, and lets you turn each cleanup step off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; each response identifies the result with X-Page-Verdict and X-Billed headers.

For PDF output, use its PDF options for paper size, margins, landscape mode, and page ranges. Other relevant controls include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets or custom viewports, retina scale, custom CSS and JavaScript, clicks before capture, selector hiding, waits for a selector, delay or network idle, request/resource blocking, headers, cookies, user agent, Authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTL, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification.

One-call cURL example

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 documentation for PDF parameters, authentication, and the complete option list. The same endpoint also supports the parameter names used by other screenshot APIs, which can simplify migration.

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

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo’s free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. Sign up for the free plan to try it without a card.

Frequently Asked Questions

Can I apply --margin-top only from page two?

No. wkhtmltopdf treats the CLI margin options as document-wide settings. Use a content wrapper or separate PDF renders.

Does page-break-before change the physical margin?

No. It controls pagination. Padding after the break moves content but does not change the page box.

Should I use @page :first in production?

Only after testing the exact wkhtmltopdf build. Support is inconsistent, so wrapper-based or two-render workflows are safer.

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.

Why did my forced break disappear after adding a float?

A reported wkhtmltopdf issue shows page-break properties being ignored inside floated parents. Move the boundary outside the float or remove the float.

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.