CobaltPDF.Requests
CobaltPDF.Requests is a lightweight companion NuGet package containing the serialisable request and
response models used when CobaltPDF is deployed as a PDF rendering microservice.
Install it in client applications — web APIs, mobile backends, other microservices — that need to send PDF generation requests over HTTP without pulling in Chromium or Playwright themselves.
[Client Application] [PDF Rendering Service]
CobaltPDF.Requests (~50 KB) HTTP POST ──▶ CobaltPDF (full library)
PdfRequest (JSON) CobaltEngine + Chromium
PdfResponse (JSON) request.ExecuteAsync(engine)
No Chromium, No Playwright
Installation
Client (web app, mobile backend, other service):
dotnet add package CobaltPDF.Requests
Rendering service (Azure Function, ASP.NET Core, AWS Lambda, worker):
dotnet add package CobaltPDF
# CobaltPDF.Requests models are included automatically
Core Types
PdfRequest
Describes the PDF to generate. All properties are optional except either Url or Html.
| Property | Type | Default | Description |
|---|---|---|---|
Url |
string? |
— | URL to render. Mutually exclusive with Html. |
Html |
string? |
— | Raw HTML string to render. Mutually exclusive with Url. |
Landscape |
bool |
false |
Landscape orientation. |
PaperFormat |
string |
"A4" |
Paper size: "A4", "A3", "Letter", "Legal", "Tabloid". |
Margins |
PdfRequestMargins? |
null (1 cm) |
Page margins as CSS length strings (e.g. "10mm", "1cm"). |
Grayscale |
bool |
false |
Render in grayscale. |
PrintBackground |
bool |
true |
Include CSS background colours and images. |
MediaType |
string |
"screen" |
CSS media type: "screen" or "print". |
Header |
string? |
— | HTML header template printed on every page. |
Footer |
string? |
— | HTML footer template printed on every page. |
Watermark |
PdfRequestWatermark? |
— | Watermark overlay applied to every page. |
WaitStrategy |
string |
"networkIdle" |
When to trigger the PDF capture (see below). |
WaitTimeoutMs |
int |
30000 |
Timeout for the wait strategy in milliseconds. |
UserAgent |
string? |
— | Browser User-Agent string. null uses Chromium's default. |
ExtraHeaders |
Dictionary<string,string> |
{} |
HTTP headers sent with every request (navigation and sub-resources). |
CustomJs |
string? |
— | JavaScript executed on the page before capture. |
BypassCsp |
bool |
false |
Bypass the page's Content Security Policy. |
Cookies |
List<PdfRequestCookie> |
[] |
Browser cookies injected before navigation. |
LocalStorage |
Dictionary<string,string> |
{} |
localStorage values written before the page loads. |
SessionStorage |
Dictionary<string,string> |
{} |
sessionStorage values written before the page loads. |
LazyLoadPages |
int? |
null |
Viewport-heights to scroll to trigger lazy-loaded content. |
LazyLoadDelayMs |
int |
200 |
Delay between scroll steps in milliseconds. |
Encryption |
PdfRequestEncryption? |
— | Password protection and permission settings. |
Metadata |
PdfRequestMetadata? |
— | Title, author, subject, keywords embedded in the PDF. |
WaitStrategy Values
| Value | Behaviour |
|---|---|
"networkIdle" |
Wait until no network activity for 500 ms (default) |
"signal" |
Wait for window.cobaltNotifyRender() called from page JavaScript |
"selector:#my-id" |
Wait until the CSS selector is present and visible |
"js:window.ready===true" |
Poll a JavaScript expression until it is truthy |
"delay:2000" |
Wait a fixed number of milliseconds |
PdfResponse
Returned by the rendering service after a successful render.
| Property | Type | Description |
|---|---|---|
Data |
string |
Base64-encoded PDF binary. |
SizeBytes |
long |
Size of the PDF in bytes. |
RenderedAt |
DateTime |
UTC timestamp of when the render completed. |
Helper methods:
byte[] bytes = response.ToBytes(); // decode to raw bytes
MemoryStream stream = response.ToStream(); // decode to a MemoryStream
Factory:
// On the server, wrap raw bytes into a PdfResponse for JSON transport
var response = PdfResponse.FromBytes(pdf.BinaryData);
Full JSON Schema
{
"url": "https://example.com/invoice/42",
"html": null,
"landscape": false,
"paperFormat": "A4",
"grayscale": false,
"printBackground": true,
"mediaType": "screen",
"margins": {
"top": "10mm",
"bottom": "10mm",
"left": "15mm",
"right": "15mm"
},
"header": "<div style='font-size:10px;text-align:center;width:100%'>My Company</div>",
"footer": "<div style='font-size:10px;text-align:center;width:100%'>Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>",
"watermark": {
"html": "<div style='font-size:80px;color:red;font-family:sans-serif;'>DRAFT</div>",
"rotation": -45,
"opacity": 0.2,
"vertical": "middle",
"horizontal": "center"
},
"waitStrategy": "networkIdle",
"waitTimeoutMs": 30000,
"userAgent": "Mozilla/5.0 (compatible; MyReportBot/1.0)",
"extraHeaders": {
"Authorization": "Bearer eyJhbGci...",
"Accept-Language": "en-GB,en;q=0.9"
},
"customJs": "document.querySelector('.no-print')?.remove();",
"bypassCsp": false,
"cookies": [
{ "name": "session", "value": "abc123", "domain": "example.com", "path": "/", "secure": true }
],
"localStorage": { "theme": "dark" },
"sessionStorage": { "userId": "42" },
"lazyLoadPages": 3,
"lazyLoadDelayMs": 200,
"encryption": {
"userPassword": "open123",
"ownerPassword": null,
"allowPrinting": true,
"allowCopying": false,
"allowModification": false
},
"metadata": {
"title": "Invoice #42",
"author": "Billing System",
"subject": "Monthly Invoice",
"keywords": "invoice, billing, 2026",
"creator": "MyApp"
}
}
Client Examples
C# — HttpClient
using CobaltPdf.Requests;
using System.Net.Http.Json;
var request = new PdfRequest
{
Url = "https://myapp.com/invoice/42",
PaperFormat = "A4",
Header = "<div style='font-size:10px;text-align:center;width:100%'>My Company</div>",
Footer = "<div style='font-size:10px;text-align:center;width:100%'>" +
"Page <span class='pageNumber'></span> of <span class='totalPages'></span></div>",
Metadata = new() { Title = "Invoice #42", Author = "Billing System" },
Encryption = new() { UserPassword = "open123", AllowPrinting = true },
Cookies = [new() { Name = "session", Value = "abc123", Domain = "myapp.com" }]
};
var httpResponse = await httpClient.PostAsJsonAsync("https://pdf-service/api/pdf", request);
httpResponse.EnsureSuccessStatusCode();
// Option A — raw PDF bytes (Content-Type: application/pdf)
byte[] pdfBytes = await httpResponse.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("invoice.pdf", pdfBytes);
// Option B — PdfResponse JSON (Content-Type: application/json)
var pdfResponse = await httpResponse.Content.ReadFromJsonAsync<PdfResponse>();
byte[] pdfBytes2 = pdfResponse!.ToBytes();
TypeScript / JavaScript — fetch
const request = {
url: "https://myapp.com/invoice/42",
paperFormat: "A4",
landscape: false,
metadata: { title: "Invoice #42", author: "Billing System" }
};
// Raw PDF response
const response = await fetch("https://pdf-service/api/pdf", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request)
});
// Stream directly into a browser download
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "invoice.pdf";
a.click();
Python — requests
import requests
payload = {
"url": "https://myapp.com/invoice/42",
"paperFormat": "A4",
"landscape": False,
"metadata": { "title": "Invoice #42", "author": "Billing System" }
}
response = requests.post("https://pdf-service/api/pdf", json=payload)
response.raise_for_status()
with open("invoice.pdf", "wb") as f:
f.write(response.content)
Server Examples
Azure Function (Isolated Worker — .NET 8)
This is the recommended pattern for Azure. The function registers CobaltEngine via DI so the browser
pool survives across invocations on a Premium Plan instance.
Warning
The Consumption Plan is not supported. Use the Premium Plan (EP1 or higher) or the Dedicated (App Service) Plan so the process is not torn down between requests.
Program.cs — host configuration:
using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using Microsoft.Extensions.Hosting;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
// Registers CobaltEngine as a singleton with the Azure cloud preset
services.AddCobaltPdf(o => CloudEnvironment.ConfigureForAzure(o));
})
.Build();
// Activate license and pre-warm the browser pool before accepting requests
CobaltEngine.SetLicense(Environment.GetEnvironmentVariable("COBALTPDF_LICENSE")!);
await CobaltEngine.PreWarmAsync();
await host.RunAsync();
PdfFunction.cs — the HTTP-triggered function:
using CobaltPdf;
using CobaltPdf.Requests;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using System.Net;
public class PdfFunction
{
private readonly CobaltEngine _renderer;
public PdfFunction(CobaltEngine renderer) => _renderer = renderer;
[Function("GeneratePdf")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "pdf")]
HttpRequestData req,
CancellationToken cancellationToken)
{
var request = await req.ReadFromJsonAsync<PdfRequest>();
if (request is null)
{
var bad = req.CreateResponse(HttpStatusCode.BadRequest);
await bad.WriteStringAsync("Invalid or missing request body.");
return bad;
}
// ExecuteAsync maps every PdfRequest property to the CobaltEngine fluent API
var pdf = await request.ExecuteAsync(_renderer, cancellationToken);
// Return raw PDF bytes
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "application/pdf");
response.Headers.Add("Content-Disposition", "attachment; filename=output.pdf");
await response.Body.WriteAsync(pdf.BinaryData, cancellationToken);
return response;
}
// Optional: return PdfResponse JSON instead for service-to-service calls
[Function("GeneratePdfJson")]
public async Task<HttpResponseData> RunJson(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "pdf/json")]
HttpRequestData req,
CancellationToken cancellationToken)
{
var request = await req.ReadFromJsonAsync<PdfRequest>();
if (request is null)
{
var bad = req.CreateResponse(HttpStatusCode.BadRequest);
await bad.WriteStringAsync("Invalid or missing request body.");
return bad;
}
var pdf = await request.ExecuteAsync(_renderer, cancellationToken);
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(PdfResponse.FromBytes(pdf.BinaryData), cancellationToken);
return response;
}
}
Dockerfile for Linux Azure Functions:
FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0 AS base
# Chromium system library dependencies
RUN apt-get update && apt-get install -y \
libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \
libxfixes3 libxrandr2 libgbm1 libasound2 \
--no-install-recommends && rm -rf /var/lib/apt/lists/*
WORKDIR /home/site/wwwroot
COPY ./publish .
Publish and deploy:
dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish
az functionapp deployment source config-zip \
--resource-group myRG \
--name my-pdf-function \
--src publish.zip
Set the license key as an Application Setting:
az functionapp config appsettings set \
--resource-group myRG \
--name my-pdf-function \
--settings COBALTPDF_LICENSE="YOUR-LICENSE-KEY"
AWS Lambda (Custom Container Runtime)
Lambda's stateless execution model means the browser pool may be recreated on each cold start. For sustained throughput, prefer ECS / Fargate instead.
Warning
Lambda imposes a 512 MB /tmp limit and does not guarantee process reuse between invocations.
Use ConfigureForLowMemory to set MaxSize = 1 and enable --single-process mode.
LambdaEntryPoint.cs:
using Amazon.Lambda.AspNetCoreServer;
namespace MyPdfService;
public class LambdaEntryPoint : APIGatewayProxyFunction
{
protected override void Init(IWebHostBuilder builder)
=> builder.UseStartup<Startup>();
}
Startup.cs (or Program.cs with minimal hosting):
using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using CobaltPdf.Requests;
var builder = WebApplication.CreateBuilder(args);
// Low-memory preset: MaxSize=1, --single-process, suitable for Lambda
builder.Services.AddCobaltPdf(o => CloudEnvironment.ConfigureForLowMemory(o));
var app = builder.Build();
CobaltEngine.SetLicense(Environment.GetEnvironmentVariable("COBALTPDF_LICENSE")!);
await CobaltEngine.PreWarmAsync();
app.MapPost("/api/pdf", async (PdfRequest request, CobaltEngine renderer, CancellationToken ct) =>
{
var pdf = await request.ExecuteAsync(renderer, ct);
return Results.File(pdf.BinaryData, "application/pdf", "output.pdf");
});
app.MapPost("/api/pdf/json", async (PdfRequest request, CobaltEngine renderer, CancellationToken ct) =>
{
var pdf = await request.ExecuteAsync(renderer, ct);
return Results.Ok(PdfResponse.FromBytes(pdf.BinaryData));
});
app.Run();
Dockerfile for AWS Lambda:
FROM public.ecr.aws/lambda/dotnet:8 AS base
# Chromium system library dependencies (Amazon Linux 2023)
RUN yum install -y \
nss atk at-spi2-atk cups-libs libdrm \
libxkbcommon libXcomposite libXdamage \
libXfixes libXrandr mesa-libgbm alsa-lib \
&& yum clean all
WORKDIR /var/task
COPY ./publish .
CMD ["MyPdfService::MyPdfService.LambdaEntryPoint::FunctionHandlerAsync"]
Recommended Lambda configuration:
| Setting | Value |
|---|---|
| Memory | 2048 MB |
| Timeout | 60 seconds |
| Architecture | x86_64 |
Publish and push:
dotnet publish -c Release -r linux-x64 --self-contained false -o ./publish
docker build -t my-pdf-lambda .
docker tag my-pdf-lambda:latest <account>.dkr.ecr.<region>.amazonaws.com/my-pdf-lambda:latest
docker push <account>.dkr.ecr.<region>.amazonaws.com/my-pdf-lambda:latest
Set the license key via AWS Secrets Manager or an environment variable in your function configuration.
AWS ECS / Fargate
ECS is the recommended AWS deployment for sustained PDF workloads. The long-running container keeps the browser pool alive across requests.
Program.cs:
using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using CobaltPdf.Requests;
var builder = WebApplication.CreateBuilder(args);
// ECS preset: omits --single-process for better stability under sustained load
builder.Services.AddCobaltPdf(o =>
{
CloudEnvironment.ConfigureForAwsEcs(o);
o.MaxSize = Math.Max(2, Environment.ProcessorCount);
});
var app = builder.Build();
CobaltEngine.SetLicense(Environment.GetEnvironmentVariable("COBALTPDF_LICENSE")!);
await CobaltEngine.PreWarmAsync();
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(() =>
CobaltEngine.ShutdownAsync().GetAwaiter().GetResult());
app.MapPost("/api/pdf", async (PdfRequest request, CobaltEngine renderer, CancellationToken ct) =>
{
var pdf = await request.ExecuteAsync(renderer, ct);
return Results.File(pdf.BinaryData, "application/pdf", "output.pdf");
});
app.Run();
ECS Task Definition recommendations:
| Resource | Minimum | Recommended |
|---|---|---|
| vCPU | 0.5 | 1.0 |
| Memory | 1 GB | 2 GB |
linuxParameters.sharedMemorySize |
— | 256 MB |
Set the license key as an ECS environment variable or retrieve it from AWS Secrets Manager:
{
"environment": [
{ "name": "COBALTPDF_LICENSE", "value": "YOUR-LICENSE-KEY" }
]
}
ASP.NET Core Minimal API
The simplest self-hosted rendering service — suitable for Docker, Azure App Service, or a Linux VM.
using CobaltPdf;
using CobaltPdf.Configuration;
using CobaltPdf.Extensions;
using CobaltPdf.Requests;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCobaltPdf(o =>
{
CloudEnvironment.ConfigureForDocker(o);
o.MaxSize = Math.Max(2, Environment.ProcessorCount);
});
var app = builder.Build();
CobaltEngine.SetLicense(app.Configuration["CobaltPdf:LicenseKey"]!);
await CobaltEngine.PreWarmAsync();
// Graceful shutdown — closes browser pool cleanly
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(() =>
CobaltEngine.ShutdownAsync().GetAwaiter().GetResult());
// POST /api/pdf → raw PDF bytes
app.MapPost("/api/pdf", async (PdfRequest request, CobaltEngine renderer, CancellationToken ct) =>
{
var pdf = await request.ExecuteAsync(renderer, ct);
return Results.File(pdf.BinaryData, "application/pdf", "output.pdf");
});
// POST /api/pdf/json → PdfResponse JSON (for service-to-service calls)
app.MapPost("/api/pdf/json", async (PdfRequest request, CobaltEngine renderer, CancellationToken ct) =>
{
var pdf = await request.ExecuteAsync(renderer, ct);
return Results.Ok(PdfResponse.FromBytes(pdf.BinaryData));
});
app.Run();
Returning the PDF
Your endpoint can respond in two ways:
Raw bytes — application/pdf
Best for browser downloads, mobile apps, and direct file saves.
return Results.File(pdf.BinaryData, "application/pdf", "output.pdf");
The client reads the response body directly as bytes — no JSON parsing required.
JSON — PdfResponse
Best for service-to-service calls where the caller needs to process or store the PDF programmatically. The binary is Base64-encoded inside the JSON envelope.
return Results.Ok(PdfResponse.FromBytes(pdf.BinaryData));
{
"data": "<base64-encoded PDF bytes>",
"sizeBytes": 142857,
"renderedAt": "2026-02-20T10:30:00Z"
}
Decode on the client:
var pdfResponse = await httpResponse.Content.ReadFromJsonAsync<PdfResponse>();
byte[] bytes = pdfResponse!.ToBytes();
Platform Summary
| Platform | Package on server | Preset | Notes |
|---|---|---|---|
| Azure Functions Premium | CobaltPDF |
ConfigureForAzure |
Do not use Consumption Plan |
| Azure App Service (Linux) | CobaltPDF |
ConfigureForAzure |
Set license in App Settings |
| Azure Container Apps | CobaltPDF |
ConfigureForDocker |
Standard Docker container |
| AWS ECS / Fargate | CobaltPDF |
ConfigureForAwsEcs |
Recommended for sustained load |
| AWS Lambda | CobaltPDF |
ConfigureForLowMemory |
Custom container; pool may restart |
| ASP.NET Core (Docker) | CobaltPDF |
ConfigureForDocker |
Full control, any host |
Security Considerations
- Authenticate every endpoint. The rendering service has access to any URL reachable from its network. An unauthenticated endpoint is a significant security risk.
- Azure Functions: use
AuthorizationLevel.Function(function key in the request header). - ASP.NET Core: add JWT bearer auth or API key middleware.
- Azure Functions: use
- Whitelist allowed domains if you do not control all callers, to prevent SSRF (Server-Side Request Forgery).
- Store license keys in secrets, not in source code — use Azure Key Vault, AWS Secrets Manager, or environment variables.
- Do not expose
htmlrendering publicly without sanitisation — arbitrary HTML rendering can be used to render internal URLs.
Related
- HTTP Requests — JSON schema reference and quick-start examples
- Dependency Injection —
AddCobaltPdfDI registration - Deployment — Docker, Azure, and AWS step-by-step platform guides
- Configuration — pool sizing and
CloudEnvironmentpresets - Wait Strategies — controlling the render trigger