HTML to PDF on Azure App Service (Linux) in C#
An ASP.NET Core app with SelectPdf.Universal runs on App Service for Linux as it is: nothing is installed on the server and no setting is required, on the Basic plan or above.
Compiled and run: Azure App Service Linux .NET 10 image, x64 · Linux ARM64 · Linux x64 · macOS ARM64 · Windows x64
Build it step by step.
-
1Create the project
A web project with three packages. The engine package matches the machine that builds the app, so
dotnet runworks on Windows, anddotnet publish -r linux-x64packs the Linux engine for App Service. The fonts package covers Chinese, Japanese, Korean and other scripts that the Linux image has no fonts for.<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <!-- the engine for the machine that builds the app; publishing with -r linux-x64 picks Linux, which is what App Service on Linux needs. Fonts: the Linux image has none for CJK and other scripts. --> <PropertyGroup> <_SelectPdfArch>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant())</_SelectPdfArch> <SelectPdfRid Condition="$([MSBuild]::IsOSPlatform('Windows'))">win-$(_SelectPdfArch)</SelectPdfRid> <SelectPdfRid Condition="$([MSBuild]::IsOSPlatform('Linux'))">linux-$(_SelectPdfArch)</SelectPdfRid> <SelectPdfRid Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx-$(_SelectPdfArch)</SelectPdfRid> <SelectPdfRid Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</SelectPdfRid> </PropertyGroup> <ItemGroup> <PackageReference Include="SelectPdf.Universal" Version="26.5.0" /> <PackageReference Include="SelectPdf.Universal.Native.$(SelectPdfRid)" Version="26.5.0" /> <PackageReference Include="SelectPdf.Universal.Fonts" Version="26.5.0" /> </ItemGroup> </Project> -
2A minimal web app
using SelectPdf.Universal; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); // GET /report.pdf: a PDF made on the server, with text in Latin, Greek and CJK scripts app.MapGet("/report.pdf", async (CancellationToken cancel) =>One endpoint,
GET /report.pdf. TheCancellationTokenis the request's. -
3HTML in three scripts
string html = """ <h1>Quarterly report</h1> <p>English · Deutsch · Ελληνικά</p> <p>日本語 · 中文 · 한국어</p> """;Latin, Greek and CJK text. The CJK line needs
SelectPdf.Universal.Fontson Linux; without it, those characters would print as empty boxes. -
4Convert and return the PDF
HtmlToPdf converter = new HtmlToPdf(); PdfDocument doc = await converter.ConvertHtmlStringAsync(html, cancel); byte[] pdf = doc.Save(); doc.Close(); return Results.File(pdf, "application/pdf"); }); app.Run();ConvertHtmlStringAsyncrenders the HTML andSave()returns the bytes. Without a file name,Results.Filelets the browser show the PDF. -
5Run it
dotnet run # then open the address dotnet run prints, followed by /report.pdf
The request returns
report.pdf. Without a license key, every page carries a trial watermark; setGlobalProperties.LicenseKeybefore the first conversion to remove it.
The complete Program.cs.
This is the exact file our samples pipeline compiled and ran on Azure App Service Linux .NET 10 image, x64 and Linux ARM64 and Linux x64 and macOS ARM64 and Windows x64 to produce the output on this page.
using SelectPdf.Universal;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// GET /report.pdf: a PDF made on the server, with text in Latin, Greek and CJK scripts
app.MapGet("/report.pdf", async (CancellationToken cancel) =>
{
string html = """
<h1>Quarterly report</h1>
<p>English · Deutsch · Ελληνικά</p>
<p>日本語 · 中文 · 한국어</p>
""";
HtmlToPdf converter = new HtmlToPdf();
PdfDocument doc = await converter.ConvertHtmlStringAsync(html, cancel);
byte[] pdf = doc.Save();
doc.Close();
return Results.File(pdf, "application/pdf");
});
app.Run();
Open the project on GitHub → this program and the variations below, ready to dotnet run
Deploy it.
Create a Linux plan and a .NET 10 web app, turn on Always On, and deploy the published app as a zip. Replace <app-name> with a name of your own; on Windows, create the zip with Compress-Archive publish\* app.zip.
az login az group create --name selectpdf-rg --location westeurope az appservice plan create --resource-group selectpdf-rg --name selectpdf-plan --sku P1v3 --is-linux az webapp create --resource-group selectpdf-rg --plan selectpdf-plan --name <app-name> --runtime "DOTNETCORE:10.0" az webapp config set --resource-group selectpdf-rg --name <app-name> --always-on true dotnet publish -c Release -r linux-x64 --self-contained false -o publish cd publish && zip -r ../app.zip . && cd .. az webapp deploy --resource-group selectpdf-rg --name <app-name> --src-path app.zip --type zip # then open https://<app-name>.azurewebsites.net/report.pdf
These steps, with the Windows variants, are in the documentation: selectpdf.com/pdf-library/html/Azure-App-Service.htm.
The PDF it produces.
Download report.pdf ↓ the file this sample produced, unedited
Common problems and fixes.
Characters print as boxes
Reference SelectPdf.Universal.Fonts: the App Service Linux image has no fonts for CJK and many other scripts, and a code deployment cannot install any.
Conversions are slow
Conversion is processor bound: Premium v3 converts several times faster than Basic or Standard. Turn on Always On so a request never meets a starting app.
The app was published on Windows
That works: the native package carries every library the engine needs, and SelectPdf restores the engine's execute permission, which a zip deployment loses. Publish with -r linux-x64.
Good to know.
- App Service runs SelectPdf on Windows and Linux on the Basic plan or above, with the complete feature set; Free and Shared plans are not supported. Source
- The Linux image has no fonts for Chinese, Japanese and Korean, and a code deployment cannot install any, so reference SelectPdf.Universal.Fonts. Source
- On Premium v3 a warm Linux instance converts selectpdf.com in about 1.3 seconds; Basic and Standard take about four times longer. Source
- Always On keeps the application loaded while idle, so the engine stays warm. Source
Questions, answered.
Short answers about this guide. The SelectPdf.Universal documentation covers every class and option.