Faster Headers and Footers on Large PDFs
Adding headers and footers to a very large PDF in a single pass is slow. Process the document in fixed-size page chunks, render each batch, then merge the parts back together to reduce total rendering time while keeping page numbers continuous across the whole document.
Solution
1. Split the page indexes into batches
Pick a chunk size and group the document's page indexes into fixed-size batches, for example 300 pages each. Smaller chunks render faster individually but add more merge work at the end.
2. Copy each batch into its own document
Extract the pages for the current batch with CopyPages, giving you a standalone document to render.
3. Render headers and footers with continuous numbering
Set FirstPageNumber to the chunk's starting page so numbering carries across the whole document instead of restarting at 1 in each chunk.
4. Save each chunk and dispose it
Write each chunk to a temp file and dispose it. Releasing the chunk before the next batch keeps memory from climbing across the run.
5. Merge the chunks into the final PDF
Re-open the temp files, append them in order with AppendPdf, save the result, then delete the temp files.
using IronPdf;
var pdf = PdfDocument.FromFile("input.pdf");
string headerHtml = "<div style='font-size:10px;'>Header</div>";
string footerHtml = "<div style='text-align:right;font-size:10px;'>Page {page} of {total-pages}</div>";
int chunkSize = 300;
var pageChunks = Enumerable.Range(0, pdf.PageCount)
.GroupBy(i => i / chunkSize)
.Select(g => g.ToArray())
.ToList();
var tempFiles = new List<string>();
foreach (var indexes in pageChunks)
{
var chunkPdf = pdf.CopyPages(indexes);
int globalStartPage = indexes.First() + 1;
chunkPdf.AddHtmlHeadersAndFooters(new ChromePdfRenderOptions
{
FirstPageNumber = globalStartPage, // keeps numbering continuous
HtmlHeader = new HtmlHeaderFooter { HtmlFragment = headerHtml, MaxHeight = 40 },
HtmlFooter = new HtmlHeaderFooter { HtmlFragment = footerHtml, MaxHeight = 40 }
});
string tempFile = Path.GetTempFileName() + ".pdf";
chunkPdf.SaveAs(tempFile);
chunkPdf.Dispose();
tempFiles.Add(tempFile);
}
// Merge the chunks back into one document.
var finalPdf = new PdfDocument(tempFiles.First());
foreach (var file in tempFiles.Skip(1))
{
using var part = new PdfDocument(file);
finalPdf.AppendPdf(part);
}
finalPdf.SaveAs("output.pdf");
finalPdf.Dispose();
// Clean up temp files.
foreach (var file in tempFiles)
File.Delete(file);
FirstPageNumber is the key: it is set to each chunk's global starting page, so a footer like Page {page} of {total-pages} stays consistent from the first chunk to the last.
Debug Tips
- Tune the chunk size: the value above (300) is adjustable, and the point at which chunking starts to pay off depends on your workload.
- Account for the merge pass: total time is per-chunk rendering plus the final merge, so factor that extra pass into any comparison.
- Keep memory in check on very large jobs: periodically forcing garbage collection during the merge and tuning the browser pool with
BrowserPool.MaxIdleTabsandMaxDynamicHFPagesPerBatchcan help.

Curtis Chau holds a Bachelor’s degree in Computer Science (Carleton University) and specializes in front-end development with expertise in Node.js, TypeScript, JavaScript, and React. Passionate about crafting intuitive and aesthetically pleasing user interfaces, Curtis enjoys working with modern frameworks and creating well-structured, visually appealing manuals.