Developers using wkhtmltopdf frequently encounter a frustrating issue: the tool completes without error but produces a blank or nearly empty PDF file. The converted document may show only a white page, contain a 2KB file with no content, or display partial rendering with missing sections. This problem has generated hundreds of GitHub issues and Stack Overflow questions, yet many cases remain unresolved because wkhtmltopdf was archived in January 2023 and will never receive fixes for these issues.
The Problem
wkhtmltopdf's blank output problem manifests in several ways. Some users report completely empty PDFs where the file size indicates zero or minimal content. Others see PDFs that render correctly in browsers but produce blank pages when converted. The most frustrating variation involves intermittent failures where the same HTML sometimes works and sometimes produces empty output.
The root cause typically involves timing and rendering synchronization. wkhtmltopdf uses an outdated Qt WebKit rendering engine that predates modern JavaScript execution patterns. When HTML pages load content dynamically through JavaScript or AJAX, wkhtmltopdf often captures the page before that content has finished loading. The tool starts rendering immediately after the initial DOM is constructed, without waiting for asynchronous operations to complete.
This timing issue is compounded by wkhtmltopdf's architecture. Unlike modern browser-based solutions that wait for page load events and network idle states, wkhtmltopdf relies on fixed delays and manual synchronization. The --javascript-delay option exists specifically because the tool cannot intelligently detect when a page is "ready" for conversion.
Error Messages and Symptoms
Blank output rarely produces clear error messages. Common symptoms include:
Exit with code 0 (appears successful, but PDF is empty)
Warning: SSL error ignored
Loading page (1/2)
Printing pages (2/2)
Done (file is blank)
In cases where debugging is enabled:
Warning: SyntaxError: Parse error
(PDF generates but contains no visible content)
When JavaScript console output is enabled:
$ wkhtmltopdf --debug-javascript http://example.com output.pdf
Loading page (1/2)
JavaScript console message: Uncaught ReferenceError: ... is not defined
Printing pages (2/2)
Done
Who Is Affected
The blank PDF issue affects developers across multiple scenarios:
Dynamic Web Applications: Single-page applications built with React, Vue, Angular, or similar frameworks that render content client-side after the initial page load. These applications may show loading spinners or skeleton screens in the PDF instead of actual content.
AJAX-Heavy Pages: Applications that load data through API calls and inject it into the DOM after the initial render. Dashboard applications, reporting systems, and data-driven pages are particularly vulnerable.
Pages with External Resources: HTML that references external stylesheets, fonts, or scripts from CDNs. SSL certificate issues or CORS restrictions can cause silent failures where resources fail to load.
Authentication-Protected Content: Pages behind login walls that redirect unauthenticated requests. wkhtmltopdf may capture the login page instead of the intended content.
Modern CSS Layouts: Pages using Flexbox, CSS Grid, or other modern layout techniques that the outdated WebKit engine cannot render correctly, resulting in elements positioned outside the visible area.
Evidence from the Developer Community
The wkhtmltopdf GitHub repository accumulated thousands of issues related to blank output before being archived. These issues span multiple years and remain unresolved.
Timeline
| Date | Event | Source |
|---|---|---|
| 2016-05-14 | "outputing empty pdf" reported | GitHub Issue #2177 |
| 2017-01-28 | "empty pdf" issue filed with workaround requests | GitHub Issue #2540 |
| 2017-07-15 | "Empty page at end of PDF" documented | GitHub Issue #3088 |
| 2018-01-10 | "Empty pdf when saving a website to pdf" reported | GitHub Issue #3086 |
| 2018-09-04 | "I got an empty PDF document but in browser is normal" | GitHub Issue #3273 |
| 2020-06-09 | Version 0.12.6 released (final release) | GitHub Releases |
| 2023-01-02 | Repository archived, no further fixes | GitHub |
Community Reports
"If I try to render an html page served by localhost everything is ok. But if I try to render the same pdf directly from file I obtain an empty pdf."
— Developer, wkhtmltopdf-general mailing list
The distinction between localhost and file-based rendering reveals wkhtmltopdf's inconsistent handling of content origins.
"wkhtmltopdf 0.12.2.4 (with patched qt) on Windows XP can return an empty pdf when converting certain URLs, even though printing from Chrome works fine."
— Issue #2540, GitHub
This demonstrates the rendering gap between wkhtmltopdf's outdated engine and modern browsers.
"Having put in the debug, I can see there's a warning for a parse error in the page's javascript... Blank pages even with a long javascript-delay."
— wkhtmltopdf-general mailing list
JavaScript parsing errors can silently cause blank output without clear indication of the failure cause.
Root Cause Analysis
Blank PDF output from wkhtmltopdf stems from several technical limitations:
1. JavaScript Timing Issues
wkhtmltopdf cannot intelligently wait for JavaScript execution to complete. The --javascript-delay option provides a fixed wait time, but this approach has fundamental problems:
- Too short a delay captures the page before content loads
- Too long a delay wastes time on fast pages
- Network latency variations make any fixed value unreliable
- The delay does not respond to actual page readiness
The --window-status option offers an alternative, allowing JavaScript to signal readiness by setting window.status. However, this requires modifying the HTML being converted, which is not always possible.
2. AJAX Content Not Loading
Modern web applications load data asynchronously. A typical pattern:
- Initial HTML loads with loading indicators
- JavaScript executes and makes API calls
- API responses arrive and content is injected into DOM
- Page becomes "ready" for viewing
wkhtmltopdf may capture at step 1 or 2, producing a PDF of loading spinners rather than actual content.
3. SSL Certificate Failures
wkhtmltopdf may silently skip HTTPS resources when SSL certificate validation fails:
Warning: SSL error ignored
Failed to load https://cdn.example.com/styles.css
Without stylesheets, the page may render with content positioned off-screen or invisible.
4. Authentication and Redirects
When wkhtmltopdf requests a protected page, the server may redirect to a login page. The resulting PDF contains the login form instead of the expected content. Cookie-based session management is complex to configure correctly.
5. CORS Restrictions
When HTML makes cross-origin requests, wkhtmltopdf may fail to load those resources due to CORS policies that do not account for the tool's unusual request patterns.
6. Missing DOCTYPE Declaration
The absence of <!DOCTYPE html> can cause wkhtmltopdf to enter quirks mode, resulting in unpredictable rendering including blank output.
7. Content Size Zero
If the HTML body contains only absolutely positioned elements or content that flows outside the document bounds, the PDF may appear blank despite technically containing elements.
Attempted Workarounds
The developer community has tried numerous approaches to fix blank output, with varying success.
Workaround 1: Increasing JavaScript Delay
Approach: Use --javascript-delay to wait longer for content to load.
wkhtmltopdf --javascript-delay 5000 http://example.com output.pdf
Limitations:
- Arbitrary delay values are unreliable across different pages
- Network latency can exceed any fixed delay
- Slows conversion significantly for pages that load quickly
- Does not address fundamental timing architecture issues
Workaround 2: Window Status Signaling
Approach: Use --window-status combined with JavaScript that signals when the page is ready.
wkhtmltopdf --window-status ready http://example.com output.pdf
With corresponding JavaScript in the page:
window.onload = function() {
setTimeout(function() {
window.status = 'ready';
}, 1000);
};
Limitations:
- Requires modifying the HTML being converted
- Not applicable when converting external websites
- Interaction with
--javascript-delayis confusing and poorly documented - Some users report the combination hangs indefinitely
Workaround 3: Using run-script for Timing
Approach: Inject JavaScript to delay rendering.
wkhtmltopdf --run-script 'window.setTimeout(function(){window.status="FOOBAR";},2000);' --window-status 'FOOBAR' http://example.com output.pdf
Limitations:
- Complex command-line syntax prone to quoting issues
- Fixed timeout still unreliable
- Does not wait for actual content loading
Workaround 4: SSL Certificate Bypass
Approach: Ignore SSL errors to load HTTPS resources.
wkhtmltopdf --ssl-protocol any --ignore-ssl-errors http://example.com output.pdf
Limitations:
- Security risk in production environments
- May not resolve all SSL-related loading issues
- Does not address underlying resource loading problems
Workaround 5: Using Xvfb on Headless Servers
Approach: Run wkhtmltopdf with a virtual framebuffer.
xvfb-run wkhtmltopdf http://example.com output.pdf
Limitations:
- Adds complexity and dependencies
- Does not address JavaScript timing issues
- Can cause additional memory and process management problems
A Different Approach: IronPDF
For developers who need consistent PDF output from dynamic web content, IronPDF provides an architectural solution to the blank output problem. Rather than wrapping an external command-line tool with fixed timing mechanisms, IronPDF embeds a Chromium rendering engine that uses the same intelligent page loading detection as modern browsers.
Why IronPDF Avoids Blank Output Issues
The fundamental difference is how IronPDF determines when a page is ready for conversion:
Network Idle Detection: IronPDF waits until network activity settles, ensuring AJAX calls complete before rendering.
Modern Event Handling: The Chromium engine understands
DOMContentLoaded,load, and other standard events that signal page readiness.Intelligent Resource Loading: External stylesheets, fonts, and scripts load through Chromium's standard resource loading pipeline with proper error handling.
JavaScript Compatibility: ES6+, modern frameworks, and asynchronous patterns execute correctly because IronPDF uses a current rendering engine.
Configurable Wait Conditions: Developers can specify wait-for-element conditions or custom JavaScript readiness checks when needed.
Code Example: Handling Dynamic Content
The following example demonstrates converting content that loads dynamically via JavaScript:
using IronPdf;
public class DynamicContentConverter
{
public byte[] ConvertDynamicPage(string url)
{
var renderer = new ChromePdfRenderer();
// Enable JavaScript execution
renderer.RenderingOptions.EnableJavaScript = true;
// Wait for network to become idle (handles AJAX automatically)
renderer.RenderingOptions.WaitFor.NetworkIdle();
// Alternative: wait for specific element to appear
// renderer.RenderingOptions.WaitFor.HtmlElementById("content-loaded");
// Render the page - Chromium handles timing automatically
using (var pdf = renderer.RenderUrlAsPdf(url))
{
return pdf.BinaryData;
}
}
}
This code handles the exact scenarios that cause wkhtmltopdf to produce blank output. The Chromium engine waits for network activity to complete before capturing the page.
Code Example: Converting AJAX-Heavy Dashboard
For applications that load data through multiple API calls:
using IronPdf;
public class DashboardConverter
{
public void ConvertDashboard(string dashboardUrl, string outputPath)
{
var renderer = new ChromePdfRenderer();
// JavaScript must be enabled for dynamic content
renderer.RenderingOptions.EnableJavaScript = true;
// Wait for network idle with extended timeout for multiple API calls
renderer.RenderingOptions.WaitFor.NetworkIdle(5000);
// Set viewport to capture full dashboard
renderer.RenderingOptions.ViewPortWidth = 1920;
renderer.RenderingOptions.ViewPortHeight = 1080;
// Render with appropriate paper size
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.PaperOrientation = IronPdf.Rendering.PdfPaperOrientation.Landscape;
using (var pdf = renderer.RenderUrlAsPdf(dashboardUrl))
{
pdf.SaveAs(outputPath);
}
}
}
Code Example: Handling Authenticated Pages
For pages requiring authentication, IronPDF supports cookie injection:
using IronPdf;
using System.Net;
public class AuthenticatedPageConverter
{
public byte[] ConvertProtectedPage(string url, string sessionCookie)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
renderer.RenderingOptions.WaitFor.NetworkIdle();
// Set authentication cookie
renderer.RenderingOptions.CustomCookies = new Dictionary<string, string>
{
{ "session_id", sessionCookie }
};
// Alternatively, use HTTP authentication
// renderer.RenderingOptions.HttpLoginCredentials = new ChromeHttpLoginCredentials
// {
// NetworkUsername = "username",
// NetworkPassword = "password"
// };
using (var pdf = renderer.RenderUrlAsPdf(url))
{
return pdf.BinaryData;
}
}
}
Code Example: Converting Local HTML with External Resources
For HTML files that reference external stylesheets and scripts:
using IronPdf;
public class LocalHtmlConverter
{
public void ConvertLocalHtml(string htmlFilePath, string outputPath)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.EnableJavaScript = true;
renderer.RenderingOptions.WaitFor.NetworkIdle();
// Set base URL for resolving relative resource paths
var baseUrl = new Uri(htmlFilePath).GetLeftPart(UriPartial.Path);
baseUrl = baseUrl.Substring(0, baseUrl.LastIndexOf('/') + 1);
// Read HTML content
var htmlContent = System.IO.File.ReadAllText(htmlFilePath);
// Render with base URL context
using (var pdf = renderer.RenderHtmlAsPdf(htmlContent, baseUrl))
{
pdf.SaveAs(outputPath);
}
}
}
API Reference
For more details on handling dynamic content and avoiding blank output:
- ChromePdfRenderer - Main rendering class
- WaitFor Options - Network idle and element detection
- HTML to PDF Tutorial - Getting started with conversions
- Handling JavaScript Content - JavaScript execution configuration
Migration Considerations
Licensing
IronPDF is commercial software with per-developer licensing. A free trial allows evaluation. Teams should consider the cost of continued debugging and workarounds with wkhtmltopdf against the cost of a maintained solution.
API Differences
Migration from wkhtmltopdf command-line or wrapper libraries requires code changes:
| wkhtmltopdf | IronPDF Equivalent |
|---|---|
--javascript-delay 5000 |
RenderingOptions.WaitFor.NetworkIdle() |
--window-status ready |
RenderingOptions.WaitFor.JavaScript("window.ready") |
--enable-javascript |
RenderingOptions.EnableJavaScript = true |
--cookie name value |
RenderingOptions.CustomCookies["name"] = "value" |
--username --password |
RenderingOptions.HttpLoginCredentials |
| External binary required | Embedded engine (no external dependencies) |
What You Gain
- Intelligent page load detection eliminates blank output from timing issues
- Modern CSS and JavaScript support renders content correctly
- Active development means bugs get fixed
- Consistent behavior across platforms without native library management
- No need to install and configure external binaries
What to Consider
- Commercial licensing cost
- Different API patterns require code changes
- Larger deployment size due to embedded Chromium
- Different rendering engine may produce slightly different visual output
Conclusion
wkhtmltopdf's blank output problem stems from fundamental architectural limitations that cannot be fixed through command-line flags or workarounds. The tool's fixed-delay timing mechanism cannot reliably handle modern dynamic web content, and the project's archival in January 2023 means these issues will never be resolved. For teams experiencing blank PDF output, migration to a solution with intelligent page load detection is the path forward.
Jacob Mellor has spent 25+ years building developer tools, including IronPDF as CTO at Iron Software.
References
- outputing empty pdf - Issue #2177{:rel="nofollow"} - Early report of empty PDF output
- empty pdf - Issue #2540{:rel="nofollow"} - Discussion of blank output causes
- Empty page at end of PDF - Issue #3088{:rel="nofollow"} - Trailing blank page problems
- Empty pdf when saving a website to pdf - Issue #3086{:rel="nofollow"} - Website conversion failures
- I got an empty PDF document but in browser is normal - Issue #3273{:rel="nofollow"} - Browser vs wkhtmltopdf rendering differences
- Blank page at the end of pdf document - Issue #4190{:rel="nofollow"} - Landscape orientation blank page issues
- javascript-delay versus window-status - Issue #2616{:rel="nofollow"} - Documentation of timing options interaction
- Using --window-status and --javascript-delay never returns - Issue #2490{:rel="nofollow"} - Infinite wait issues
- wkhtmltopdf does not work using --javascript-delay with plotly.js - Issue #2721{:rel="nofollow"} - JavaScript framework compatibility
- PDF Generated is login screen not the route passed - KnpSnappyBundle Issue #120{:rel="nofollow"} - Authentication redirect issues
- Redirection Errors? No page or blank page - Issue #2346{:rel="nofollow"} - Redirect handling problems
- wkhtmltopdf is now an abandonware - Doppio Documentation{:rel="nofollow"} - Project status assessment
- wkhtmltopdf GitHub Repository{:rel="nofollow"} - Archived January 2, 2023
For IronPDF documentation and tutorials, visit ironpdf.com.
Top comments (0)