Why You Should Not Upload PDFs to Merge Tools
HTTPS protects a PDF while it travels to a service. It does not make the service local. Once the file lands on a remote system, its retention rules, logs, backups, subprocessors, and access controls all become your problem. For an NDA, a client contract, or a medical record, merging the pages right in the browser is often the cleaner privacy call. We dig into the tradeoff in detail in our client-side versus server-side processing guide.
Need to combine the files now?
Open the PDF merge tool, arrange the files in order, and download one PDF. The merge runs in your browser, so the documents stay on your device.
Merge PDF files locallyBe precise about what βsecureβ means when a big-name merger advertises 256-bit encryption. That claim almost always describes encryption in transit, the SSL/TLS connection your browser used to reach the server. It says nothing about what happens to the file after it arrives. Under GDPR, uploading a document with personal data makes your organization the data controller and the tool provider the data processor. That relationship legally requires a signed Data Processing Agreement. Most free merge tools do not offer one. A browser-based merger dodges the whole question, since no data leaves the tab and there is no processor relationship to document.
Upload a PDF to an online tool and the file travels to a server. For contracts, legal documents, medical records, or financial statements, that upload is a real risk. You cannot verify any of this:
- Whether the server deletes the file after processing
- What jurisdiction the server is in (relevant to GDPR compliance)
- Whether the upload is logged in the tool's analytics or audit trail
- Who has access to the server and its stored files
Browser-based merging kills every one of those concerns. Your browser's JavaScript engine reads the PDF bytes, processes them in local RAM, and writes the result back to disk through a download prompt. Nothing leaves the tab. If the idea of never uploading is new to you, our piece on not uploading files to random websites makes the case in full.
How PDFs Work Internally
A PDF file is not just a flat sequence of pages. It is structured as a hierarchy of objects:
PDF file structure (simplified):ββββββββββββββββββββββββββββββββββββββββ Header: %PDF-1.7 ββββββββββββββββββββββββββββββββββββββββ€β Body: Objects (pages, fonts, ββ images, annotations) ββ 1 0 obj β Catalog (root) ββ 2 0 obj β Pages tree root ββ 3 0 obj β Page 1 ββ 4 0 obj β Page 2 ββ 5 0 obj β Font resource ββββββββββββββββββββββββββββββββββββββββ€β Cross-Reference Table (XREF): ββ Maps object numbers β byte offsetsββ Enables random access to pages ββββββββββββββββββββββββββββββββββββββββ€β Trailer: Points to XREF table start ββββββββββββββββββββββββββββββββββββββββWhere the Format Actually Comes From
PDF was not always an open standard. Adobe created it in 1993 as a proprietary format and only handed control to a standards body in 2008, when it became ISO 32000. That history explains why so many PDF tools, Adobe's own products included, still carry odd legacy behavior. The format spent fifteen years evolving inside one company before anyone else could build against it. The object-and-xref structure above has barely changed since the early versions. That stability is exactly why pdf-lib can handle PDFs from wildly different software (Word exports, scanner firmware, LaTeX, InDesign) with one consistent model.
Merging PDFs means building a new document, copying every page object from each source PDF into the new document's object pool, and writing a fresh XREF table that points to them all. No image re-encoding. No re-rendering. Just object-level copying and index rebuilding.pdf-libdoes all of it.
That object-level copying matters more than it sounds. Some merge tools, usually the older or cheaper ones, rasterize each page instead. They render it to an image, then wrap the images in a new PDF, rather than copying the real vector and text objects. The output looks fine at a glance. Then you notice the text is blurry, the file is huge, and you can no longer select or search a word of it. Merge a set of PDFs and find Ctrl+F suddenly returns nothing? That is the tell. The tool rasterized instead of merging properly. This is the same failure mode covered in our guide to why PDFs get bigger after compression.
Basic Merge with pdf-lib
Select PDFs
Read locally from disk
Merge locally
pdf-lib copies pages in RAM
Download
Write only to your device
import { PDFDocument } from 'pdf-lib'; async function mergePDFs(files: File[]): Promise<Blob> { // Step 1: Create the output document const mergedDoc = await PDFDocument.create(); for (const file of files) { // Step 2: Read each file as ArrayBuffer const arrayBuffer = await file.arrayBuffer(); // Step 3: Load the source PDF const sourcePdf = await PDFDocument.load(arrayBuffer, { ignoreEncryption: false, // Reject encrypted PDFs }); // Step 4: Copy all pages into the merged document const pageCount = sourcePdf.getPageCount(); const copiedPages = await mergedDoc.copyPages( sourcePdf, [...Array(pageCount).keys()] // [0, 1, 2, ..., pageCount-1] ); // Step 5: Add each copied page in order copiedPages.forEach(page => mergedDoc.addPage(page)); } // Step 6: Serialize to Uint8Array const mergedBytes = await mergedDoc.save(); // Step 7: Return as downloadable Blob return new Blob([mergedBytes], { type: 'application/pdf' });} // Usageconst fileInput = document.getElementById('files') as HTMLInputElement;fileInput.addEventListener('change', async () => { const files = Array.from(fileInput.files ?? []); if (files.length < 2) { alert('Select at least 2 PDF files'); return; } const merged = await mergePDFs(files); const url = URL.createObjectURL(merged); const link = document.createElement('a'); link.href = url; link.download = 'merged.pdf'; link.click(); URL.revokeObjectURL(url);});Merging Specific Pages, Not Entire Documents
Sometimes you need only certain pages from each source PDF, not the whole document.copyPages accepts any array of page indices:
// Copy only pages 1, 3, and 5 from a PDF (0-indexed: 0, 2, 4)const selectedPages = await mergedDoc.copyPages(sourcePdf, [0, 2, 4]);selectedPages.forEach(page => mergedDoc.addPage(page)); // Copy pages in reverse order (reverse the whole document)const allIndices = [...Array(sourcePdf.getPageCount()).keys()].reverse();const reversedPages = await mergedDoc.copyPages(sourcePdf, allIndices);reversedPages.forEach(page => mergedDoc.addPage(page)); // Copy last page onlyconst lastPage = sourcePdf.getPageCount() - 1;const [lastCopy] = await mergedDoc.copyPages(sourcePdf, [lastPage]);mergedDoc.addPage(lastCopy);Setting Merged Document Metadata
These fields map to the standard Dublin Core metadata elements that most PDF readers and search tools already know how to index, so filling them in properly actually makes the merged file more discoverable, not just tidier.
// Set document metadata after mergingmergedDoc.setTitle('Merged Contracts β Q3 2026');mergedDoc.setAuthor('Azeem Mustafa');mergedDoc.setSubject('Contract bundle for review');mergedDoc.setKeywords(['contracts', 'NDA', '2026']);mergedDoc.setCreator('FileMint PDF Merger');mergedDoc.setCreationDate(new Date());mergedDoc.setModificationDate(new Date());Problems You Will Hit and How to Fix Them
Encrypted / Password-Protected PDFs
pdf-lib throws when trying to load an encrypted PDF:Error: Input document to PDFDocument.load is encrypted.
// Handle encrypted PDFs gracefullytry { const sourcePdf = await PDFDocument.load(arrayBuffer);} catch (err) { if (err instanceof Error && err.message.includes('encrypted')) { // Show user-friendly error β they need to unlock the PDF first console.error('This PDF is password-protected. Remove the password before merging.'); // Options: // 1. Ask user to re-export without password protection // 2. Use a PDF unlock tool first (local, privacy-preserving) }}Large Files and Memory Limits
Chrome's V8 heap limit means a giant merge can crash the tab. For PDFs over 50MB each, process them one at a time and free memory between files:
// Memory-conscious merge: process one file at a time// and release the source document reference immediatelyfor (const file of files) { const arrayBuffer = await file.arrayBuffer(); let sourcePdf = await PDFDocument.load(arrayBuffer); const indices = [...Array(sourcePdf.getPageCount()).keys()]; const pages = await mergedDoc.copyPages(sourcePdf, indices); pages.forEach(p => mergedDoc.addPage(p)); // Explicitly null the reference to help the garbage collector // (This is best-effort β V8 GC timing is not guaranteed) sourcePdf = null as unknown as PDFDocument; // For very large sets, yield to the event loop between files await new Promise(resolve => setTimeout(resolve, 0));}Scanned PDFs Are Just Images
A scanned PDF is just rasterized page images. There is no searchable text in it. pdf-lib merges scanned PDFs fine. But if you need to search, copy, or index the content, run OCR before merging. No client-side JavaScript library ships full OCR. You need either Tesseract.js (open source, slower) or a cloud OCR API.
Form Fields After Merging
pdf-lib copies form fields, but if two source PDFs have fields with identical names, they will conflict in the merged document. Flatten form fields before merging if the forms are already filled:
// Flatten all form fields in a PDF (makes them non-editable but merge-safe)const form = sourcePdf.getForm();form.flatten(); // Rasterises field values into page content // Now copy pages β no field name conflicts in the merged outputconst pages = await mergedDoc.copyPages(sourcePdf, indices);When to Use Each Approach
| Scenario | Recommended Tool |
|---|---|
| Merging contracts or confidential documents | Browser-based (pdf-lib) β local, local processing |
| Files totalling over 300MB | Node.js pdf-lib script or server-side tool |
| Non-sensitive documents, occasional use | Any tool β browser-based preferred for speed |
| Password-protected PDFs | Remove password first with a local tool, then merge |
| Scanned PDFs needing OCR before merge | Tesseract.js (browser) or a server-side OCR pipeline |
Dealing with PDFs that somehow grew after compression? It happens more than you would think. Our guide on why PDFs get larger after compression walks through the font embedding and image re-encoding traps behind it. For the full set of browser-based PDF operations, our PDF transformation guide covers splitting, rotating, extracting pages, and compressing, all locally. If you handle sensitive files often, the secure document management guide is worth a read.
Merge PDFs locally: local processing, no file limit.
Drop multiple PDFs, drag to reorder, download the merged result. Your documents never leave your device.
Open PDF Merger βThe internal structure that makes merging possible (and occasionally messy) is defined in ISO 32000-2, the current PDF specification. The merge logic in tools like ours is commonly built on open libraries such as pdf-lib, which parses and reassembles the cross-reference tables entirely in JavaScript, in your browser, with nothing sent anywhere.
