How to Convert HTML to PDF in .NET
Learn how to convert HTML strings, files, and URLs to PDF in .NET with C#, Syncfusion, IronPDF, deployment guidance, troubleshooting, and ScreenshotNeo.

Converting HTML to PDF in .NET is usually a rendering problem rather than a file-format problem. Your choice depends on where the HTML comes from, whether it needs JavaScript and modern CSS, which .NET runtime and operating system you deploy on, and what licensing terms your project accepts.
For a URL or web page, use a converter with a browser-based rendering engine. For an HTML string or template that your application already owns, use the library’s string API. Syncfusion documents a Blink, Chromium-based engine and input support for URLs, HTML strings, files, MHTML, authenticated pages, and HTTP GET or POST content. IronPDF documents a ChromePdfRenderer API for rendering an HTML string and saving the resulting PDF.
Choose an approach first
| Situation | Good starting point | Questions to validate |
|---|---|---|
| You have an HTML string | IronPDF ChromePdfRenderer.RenderHtmlAsPdf or an equivalent string API |
Do templates use JavaScript, web fonts, SVG, or external assets? |
| You have a remote URL | Syncfusion HtmlToPdfConverter.Convert |
Does the page require authentication, custom headers, cookies, or a wait for client-side rendering? |
| You have a local HTML file | A file or URL conversion method from your selected library | Are relative images, fonts, and stylesheets readable by the renderer? |
| You need MHTML, POST data, or authenticated content | A converter whose documented input model supports that request | How are credentials and request data supplied, and where are they stored? |
| You need a cross-platform service | Check the exact package and renderer prerequisites | Does the package support your .NET version, OS, container, and native dependencies? |
The examples below are vendor-documented starting points, not a guarantee that every application will render identically. Test representative pages, fonts, charts, images, and long documents on the host where production will run.
Convert an HTML string with IronPDF
The current IronPDF tutorial uses ChromePdfRenderer, renders a string with RenderHtmlAsPdf, and writes the result with SaveAs. Install the package specified by the vendor for your target project, then use a small console program:

using IronPdf;
var html = """
Invoice 1042
Generated from an HTML string in .NET.
Total: $125.00
""";
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("invoice.pdf");
Use this flow when your server has already produced the final HTML. Keep assets available to the renderer: embed critical images as data URLs, use absolute URLs where appropriate, or configure the library’s base URL according to its documentation. If your template depends on JavaScript, wait behavior and browser runtime support become part of the conversion design.
String conversion checklist
- Set a UTF-8 meta declaration so non-ASCII characters are interpreted correctly.
- Provide print CSS for page breaks, margins, and colors.
- Make external fonts and images reachable from the conversion process.
- Use deterministic data and locale settings when PDFs are compared or archived.
- Save to a controlled path or stream and handle failures around both rendering and file output.
Convert a URL with Syncfusion
Syncfusion’s documented URL flow creates an HtmlToPdfConverter, calls Convert with a URL, and saves the returned PdfDocument. The example package is Syncfusion.HtmlToPdfConverter.Net.Windows; the cited ASP.NET Core documentation states that this named package is compatible with .NET 8.0 and later. Confirm the package variant and prerequisites for your actual target before copying it.
using Syncfusion.HtmlConverter;
using Syncfusion.Pdf;
var converter = new HtmlToPdfConverter();
PdfDocument document = converter.Convert("https://example.com");
document.Save("example.pdf");
document.Close(true);
A URL conversion is useful when the page already exists and must be printed as a visitor would see it. Rendering behavior depends on network access, JavaScript execution, cookies, authentication, redirects, and the renderer’s browser runtime. A page that looks correct in your desktop browser may still need layout-specific testing in the server process.
ASP.NET Core endpoint example
using Microsoft.AspNetCore.Mvc;
using Syncfusion.HtmlConverter;
using Syncfusion.Pdf;
[ApiController]
[Route("pdf")]
public sealed class PdfController : ControllerBase
{
[HttpGet]
public IActionResult Create([FromQuery] string url)
{
if (!Uri.TryCreate(url, UriKind.Absolute, out var parsed) ||
(parsed.Scheme != Uri.UriSchemeHttp && parsed.Scheme != Uri.UriSchemeHttps))
{
return BadRequest("Provide an absolute HTTP or HTTPS URL.");
}
var converter = new HtmlToPdfConverter();
using PdfDocument document = converter.Convert(parsed.ToString());
using var output = new MemoryStream();
document.Save(output);
return File(output.ToArray(), "application/pdf", "page.pdf");
}
}
For a public service, do not accept arbitrary URLs without an application-level policy. Restrict destinations, control timeouts, and decide how redirects and private network addresses are handled. The cited material establishes conversion capabilities, not a complete security policy for untrusted URLs.
Rendering options that affect the result
JavaScript and modern CSS
Syncfusion describes Blink as Chromium-based and documents JavaScript execution. That makes it a reasonable starting point for pages that require client-side layout, but it does not promise pixel identity with every browser version. Exercise the page in the exact runtime and package you deploy.
Fonts, SVG, and images
Server rendering often fails because an asset is unavailable rather than because the HTML is invalid. Install required fonts in the host image, verify HTTPS certificates, make SVG and image URLs reachable, and inspect the generated PDF for missing glyphs. If the HTML is generated from a file, resolve relative paths against a known base location.
Headers, footers, and authenticated pages
Syncfusion’s feature documentation describes authentication, headers and footers, and conversions from HTML pages, strings, SVG, and MHTML. Map those features to your library’s exact API. Never place long-lived credentials directly in a template or query string; inject them through the supported request configuration and protect logs.
Page size and breaks
Use print-oriented CSS such as @page, break-before, break-after, and break-inside. Test tables that span pages, headings near page boundaries, wide code blocks, and images with fixed dimensions. A PDF converter may honor these rules differently from a browser’s print dialog.
Licensing and deployment checks
Package compatibility is specific. The cited Syncfusion ASP.NET Core page names .NET 8.0 and later for Syncfusion.HtmlToPdfConverter.Net.Windows; do not generalize that statement to every Syncfusion package or target. Check the package’s operating-system and native-runtime requirements before selecting a base container.
Licensing must also be completed during setup. Syncfusion states that, beginning with version 16.2.0.x, applications referencing its assemblies from trial setup or NuGet must add the licensing assembly and register a license key under the conditions in its license documentation. Confirm whether your deployment qualifies and register the key exactly as required. IronPDF’s current tutorial is vendor material as well; review its current license terms before shipping.
Production design: reliability, performance, and cost
- Measure real documents. Record render time, output size, memory use, and failure rate for your own templates and data volume. The reviewed sources do not provide an independent performance ranking.
- Separate rendering from requests. For long or JavaScript-heavy pages, queue work and return a job identifier instead of holding an HTTP request open indefinitely.
- Bound concurrency. Browser-based renderers can consume substantial CPU and memory. Use a worker limit, observe saturation, and recycle processes according to the vendor’s guidance.
- Cache deliberately. Cache only when the URL, user permissions, locale, and data are part of the cache key. Never reuse an authenticated PDF for another user.
- Make failures visible. Log a correlation ID, input type, renderer version, elapsed time, and a sanitized failure reason. Keep secrets and complete authenticated URLs out of logs.
- Control external dependencies. A remote stylesheet, font, API call, or image can make output slow or incomplete. Prefer local or bundled assets for documents that must be reproducible.
Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Blank PDF | The page is still waiting for JavaScript, or the renderer cannot load the document. | Confirm the URL is reachable from the server, wait for the page’s data to appear, and test a minimal static page. |
| Missing images or fonts | Relative paths, blocked requests, certificate errors, or missing host fonts. | Use a known base URL, verify network access, install fonts, and inspect asset responses. |
| Characters appear as boxes | The required Unicode font is absent or not embedded. | Install a font covering the character set and configure the renderer’s font behavior. |
| Authentication redirects to login | Cookies, headers, or request method were not passed to the renderer. | Use the library’s documented authenticated-page or header support and verify the session separately. |
| Tables overlap or split badly | Screen CSS does not define print behavior. | Add print CSS, explicit widths, and page-break rules; test long rows. |
| Works locally but fails in production | Different OS, package variant, native dependency, browser runtime, or installed fonts. | Reproduce with the production image and pin compatible package versions. |
| License exception | The required licensing assembly or key registration is missing. | Follow the vendor’s licensing instructions for the exact version and deployment. |
Or skip the browser setup
If your goal is simply to turn a public web page into a PDF, ScreenshotNeo provides a website screenshot API with PDF output. One GET request returns a PDF or image, so your .NET service does not need to package and operate a browser renderer.

curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d format=pdf \
-o page.pdf
See the ScreenshotNeo API documentation for request options. Cookie and consent banners are accepted and removed before capture, along with more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
Equivalent calls from .NET clients
using System.Net.Http;
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(90) };
var endpoint = "https://api.screenshotneo.com/v1/shot" +
"?access_key=YOUR_API_KEY" +
"&url=https%3A%2F%2Fstripe.com" +
"&format=pdf";
var bytes = await http.GetByteArrayAsync(endpoint);
await File.WriteAllBytesAsync("page.pdf", bytes);
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com", "format": "pdf"},
timeout=90,
)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com',
format: 'pdf'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
require('fs').writeFileSync('page.pdf', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, paper size, margins, landscape mode, page ranges, custom CSS and JavaScript, click actions, selector waits, delays, network-idle waits, request blocking, custom headers and cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Can I convert an HTML file instead of a URL?
Yes. Choose a library method that accepts a file or load the file into a string and use the string API. Resolve relative assets against the file’s directory.
Which library is faster?
The reviewed sources do not establish a comparable benchmark. Measure your real templates, concurrency, and deployment image.
Does a Chromium-based renderer guarantee browser-identical output?
No. It can improve support for JavaScript and modern CSS, but package versions, fonts, timing, and print rules still affect the result.
What should I test before production?
Test authenticated pages, missing assets, non-Latin text, long tables, charts, page breaks, redirects, timeouts, container deployment, and licensing behavior.
When should I use an API instead of a .NET library?
Use an API when you want a managed capture service, need PDF or image output from public pages, or want to avoid operating a browser runtime. Use a library when rendering must happen inside your application and you need direct control over local HTML and data.


