Run in production (ASP.NET Core)
Production deployment guide for the ModPageSpeed 2.0 ASP.NET Core middleware: secret management, health probes, license enforcement, multi-instance scale, and zero-downtime restarts.
This page is for teams shipping the WeAmp.PageSpeed.AspNetCore NuGet
middleware into a production ASP.NET Core deployment. If you are still
evaluating the middleware locally, start with
Install ASP.NET Core middleware instead.
ModPageSpeed 2.0 v1.0.0 ships only as the ASP.NET Core middleware (NuGet) plus the existing Docker / Helm images. If you need a native module for nginx, Apache, or IIS, use mod_pagespeed 1.1 — same optimization pipeline, production-hardened native server modules.
Prerequisites
- .NET 9 runtime on your production host.
- An ASP.NET Core application that serves HTML responses.
- A supported runtime identifier —
linux-x64,linux-arm64,osx-arm64, orwin-x64. The meta-package resolves the matching native asset automatically at restore time. - A valid license key from your FastSpring purchase confirmation or your account page on modpagespeed.com.
Install
Add the NuGet package to your project. CI restores the correct native library for the target RID — no per-platform branching in your build.
dotnet add package WeAmp.PageSpeed.AspNetCore
If your build container differs from the deployment target, restore with an explicit RID so the native asset matches the production host:
dotnet publish -c Release -r linux-x64 --self-contained false
Wire up the middleware
Register the services and add UsePageSpeed() early in the pipeline, before
any middleware that writes to the response body:
using WeAmp.PageSpeed.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPageSpeed(builder.Configuration);
var app = builder.Build();
app.UsePageSpeed();
app.MapHealthChecks("/healthz");
app.MapRazorPages();
app.Run();
Bind a PageSpeed section in your configuration. For the full set of
PageSpeedOptions properties and hot-reload semantics, see the
ASP.NET Core configuration reference.
Manage the license key as a secret
Never check LicenseKey into source control or appsettings.json committed
to your repo. Source it from your platform’s secret store and surface it
through ASP.NET Core’s standard configuration providers:
# Linux systemd unit
Environment=PageSpeed__LicenseKey=eyJ...
# Kubernetes (Secret mounted as env)
env:
- name: PageSpeed__LicenseKey
valueFrom:
secretKeyRef:
name: pagespeed-license
key: token
# Azure App Service / Key Vault reference
PageSpeed__LicenseKey=@Microsoft.KeyVault(SecretUri=https://...)
The double-underscore convention maps to PageSpeedOptions.LicenseKey per
ASP.NET Core’s configuration precedence — environment variables override
appsettings.json. See Activate your license
for token format details and verification.
The middleware polls the worker’s /v1/health endpoint every 30 seconds to
re-check license state. Renewals issued by the license service propagate to
running instances within that window without a restart; expired tokens cause
the middleware to fall back to pass-through automatically.
Cache backing
The middleware writes a single memory-mapped Cyclone volume file under
PageSpeed.Cache.VolumePath (default: /var/cache/pagespeed/volume.dat,
1 GB). For production:
- Set an explicit
PageSpeed__Cache__VolumePath(double-underscore for nesting) on a persistent volume so the cache survives container restarts and isn’t billed against ephemeral disk. The path is a single file, pre-sized toPageSpeed__Cache__VolumeSizeBytes. - Ensure the application user has read/write access to the file’s parent directory.
- Leave
CacheModeatSafefor the initial rollout. Safe mode addsmust-revalidateto asset responses so a misconfigured filter recovers in minutes rather than hours. Switch toAggressiveonly after a stable baseline and with a CDN purge path available — see Choose a cache mode for the full trade-off.
Validate the deployment
After deploy, confirm the middleware is active and licensed:
curl -I https://your-app.example.com/
# X-PageSpeed: WeAmp.PageSpeed/2.0.0
curl -s https://your-app.example.com/healthz | jq .
# "status": "Healthy"
# entries, version, license_active: true, license_plan: "pro"
The /healthz endpoint surfaces license_active, license_plan, and the
expiry timestamp from the polling service. When the license is missing or
expired, the check reports Degraded (HTTP 200 with a Degraded status
payload) — wire your monitoring to alert on the license_active=false
condition rather than only on Unhealthy. The worker process exposes its
own diagnostic console SPA out-of-process; bind PageSpeed.Worker.ApiPort
to expose it on a port reachable by your ops team (not by the public
internet — there is no authentication on the worker management API).
Scale across multiple instances
The license token is bound to your subscription, not to a single host. Each
instance of the same application validates the same token against the worker
health endpoint independently — there is no instance-level activation step.
Your plan’s max_instances ceiling applies to the aggregate count reported
by the license service. Stay within it when sizing replica counts; contact
support before exceeding the limit so the operator can re-mint a token with
the correct seat allocation.
Each instance maintains its own on-disk cache. There is no shared cache backing across replicas; each replica builds its own warm cache, which isolates cache state across instances.
Zero-downtime restarts
The middleware honors ASP.NET Core’s standard graceful-shutdown semantics.
On SIGTERM, in-flight requests complete against the existing cache before
the worker shuts down; new requests route to a fresh instance. Configure
your orchestrator’s terminationGracePeriodSeconds (Kubernetes) or
equivalent to at least 30 seconds so the license-poll cycle and any
mid-flight HTML rewrites complete cleanly.
For container deployments, the Deploy to production guide covers the Docker / Helm shipping path; the systemd and Kubernetes patterns there apply equally to the NuGet middleware when wrapped in your own container image.
See also
- ASP.NET Core configuration reference — full options table, hot reload, environment-specific layering.
- Activate your license — token format, troubleshooting, and renewal.
- Choose a cache mode —
SafevsAggressivetrade-offs and CDN guidance. - Troubleshoot common issues — diagnostic flow for cache, worker, and license problems.