WeAmp.PageSpeed.NativeAssets.Windows 2.0.41

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package WeAmp.PageSpeed.NativeAssets.Windows --version 2.0.41
                    
NuGet\Install-Package WeAmp.PageSpeed.NativeAssets.Windows -Version 2.0.41
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="WeAmp.PageSpeed.NativeAssets.Windows" Version="2.0.41" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WeAmp.PageSpeed.NativeAssets.Windows" Version="2.0.41" />
                    
Directory.Packages.props
<PackageReference Include="WeAmp.PageSpeed.NativeAssets.Windows" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add WeAmp.PageSpeed.NativeAssets.Windows --version 2.0.41
                    
#r "nuget: WeAmp.PageSpeed.NativeAssets.Windows, 2.0.41"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package WeAmp.PageSpeed.NativeAssets.Windows@2.0.41
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=WeAmp.PageSpeed.NativeAssets.Windows&version=2.0.41
                    
Install as a Cake Addin
#tool nuget:?package=WeAmp.PageSpeed.NativeAssets.Windows&version=2.0.41
                    
Install as a Cake Tool

WeAmp.PageSpeed for ASP.NET Core

Drop-in ASP.NET Core middleware that improves Core Web Vitals without touching your app code. It adds critical CSS inlining, LCP preload injection, lazy loading, and on-demand WebP/AVIF image transcoding to every HTML response. The C++23 PageSpeed engine runs in-process via P/Invoke and serves cache hits zero-copy.

Single-package install. Hot-reloadable config. Optimization runs out of the box, so you can evaluate it without a license. While unlicensed, responses carry an X-PageSpeed-Warn: unlicensed header. Production use requires a commercial license — but the software never locks you out.

Buy or apply a key from the in-app console at /console/ — see plans at modpagespeed.com/pricing/.

Quick Start

1. Install the package

dotnet add package WeAmp.PageSpeed.AspNetCore

The matching native binaries for your runtime (linux-x64, linux-arm64, osx-arm64, or win-x64) come in transitively: no separate NativeAssets package reference required.

2. Register the middleware in Program.cs

using WeAmp.PageSpeed.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPageSpeed();

var app = builder.Build();

app.UsePageSpeed();    // before anything that writes the response body
app.UseStaticFiles();

app.Run();

No extra wiring required: with no Worker section, the worker process starts automatically and coordinates over an auto-resolved per-process socket. Add a Worker section only to change that — see Configuration. Optimization is on by default; see step 3 to license it for production.

3. License it for production

You can evaluate the middleware right away. Production use requires a commercial license — but the software never locks you out: until a license is applied, every response carries an X-PageSpeed-Warn: unlicensed header and the console shows an unlicensed notice. To license it, run your app and navigate to http://<your-app-host>/console/ in a browser, then buy a subscription — monthly or annual, billed immediately, cancel anytime. The issued key is applied automatically and persisted to the cache volume, then reused on subsequent runs. You can also paste an existing key into the same /console/ page. (/console/ is an admin surface — see Security before exposing it beyond localhost.)

4. How do I know it's working?

Three checks, from quickest to most thorough. These examples assume your app listens on :5050 (set ASPNETCORE_URLS=http://localhost:5050 or adjust the URLs to your port) and serves at least one HTML page and one image.

Hit a content route and look for the X-PageSpeed header:

curl -i http://localhost:5050/
HTTP/1.1 200 OK
X-PageSpeed: HIT

HIT means the response was served from the optimized cache; MISS means the worker is building the variant and you'll see HIT on the next request. (The /console/* routes are short-circuited before the middleware, so they intentionally do not carry X-PageSpeed — only content routes like / and your assets do.)

Open http://localhost:5050/console/. Once a few requests have run, the Dashboard and Metrics show non-zero counts. If everything reads zero, see How do I know it's working? in the docs.

Confirm image transcoding via content negotiation. We don't rewrite URLs in 2.0: the same /hero.jpg URL serves WebP to WebP-capable clients and AVIF to AVIF-capable ones, selected by the request Accept header:

curl -s -o /dev/null -D - http://localhost:5050/hero.jpg -H 'Accept: image/jpeg'
# Content-Length: 98230   Content-Type: image/jpeg   Vary: Accept, Save-Data, User-Agent

curl -s -o /dev/null -D - http://localhost:5050/hero.jpg -H 'Accept: image/webp'
# Content-Length: 2422    Content-Type: image/webp   Vary: Accept, Save-Data, User-Agent

curl -s -o /dev/null -D - http://localhost:5050/hero.jpg -H 'Accept: image/avif'
# Content-Length: 415     Content-Type: image/avif   Vary: Accept, Save-Data, User-Agent

Same URL, materially smaller bytes, and a Vary: Accept, Save-Data, User-Agent header so caches keep the variants apart. (Byte counts are from one sample image; yours will differ.)

What It Does

  • HTML optimization: critical CSS inlining, lazy loading, LCP preload injection, third-party preconnect hints
  • Image transcoding: on-demand conversion to WebP and AVIF, viewport-aware resizing, Save-Data support
  • CSS/JS minification: whitespace removal, comment stripping
  • Zero-copy caching: cache hits served from the memory-mapped Cyclone cache with no copy, up to 36 optimized variants per resource (format × viewport × density × Save-Data)

The middleware buffers HTML responses, passes them through the native libpagespeed library, and notifies the worker process to generate optimized asset variants asynchronously.

Platform Support

RID Status Notes
linux-x64 Supported glibc 2.34+ (RHEL 9 / Ubuntu 22.04 / Debian 12 or newer)
linux-arm64 Supported glibc 2.34+
osx-arm64 Supported macOS 13+ (Apple Silicon)
win-x64 Supported Windows 10/11, Server 2019+

Native binaries (libpagespeed, factory_worker) are bundled with the matching WeAmp.PageSpeed.NativeAssets.* package, pulled in transitively by WeAmp.PageSpeed.AspNetCore. The worker process starts automatically on application boot. The Linux binaries statically link the C++ runtime (libc++/libc++abi/libunwind), so no additional shared libraries need to be present on the host beyond the system glibc.

On Linux and want a reverse proxy instead of in-process middleware? WeAmp.PageSpeed.Sidecar runs mod_pagespeed 1.15 as a bundled nginx + ngx_pagespeed reverse proxy in front of Kestrel (Linux-only). Use it when you want the classic nginx module in a sidecar; use this package for cross-platform in-process optimization with WebP/AVIF.

Each NativeAssets package also ships a BUILD_INFO.json file at runtimes/<rid>/native/BUILD_INFO.json with git_sha, git_sha_short, build_timestamp_utc, and rid. It is intended for support correlation — matching compliance-report heartbeats (which emit the worker's git_commit) to a specific package build — and for verifying package provenance without running the worker.

Configuration

Add a PageSpeed section to appsettings.json. Only Cache is shown below; every key in the table is optional and falls back to its default.

{
  "PageSpeed": {
    "Cache": {
      "VolumePath": "/var/cache/pagespeed/volume.dat",
      "VolumeSizeBytes": 1073741824
    }
  }
}

Set Cache.VolumePath to a location that is writable in your environment. The default /var/cache/pagespeed/ assumes a Linux host; on Windows, macOS, or containers without that path, point it somewhere writable (for example ./cache/volume.dat or %TEMP%).

Top-level keys:

Setting Default Description
Enabled true Enable/disable the middleware (supports hot-reload)
LicenseKey null License key (base64url-encoded Ed25519 token)
ExcludePaths ["/api/", "/signalr/", "/_blazor/", "/_framework/"] URL prefixes the middleware leaves untouched (supports hot-reload)
CacheMode Safe Safe: must-revalidate on assets. Aggressive: public + stale-if-error on assets. HTML is always no-cache.
MaxResponseBufferBytes 5242880 (5 MB) Responses larger than this pass through unmodified
CssMaxAgeSeconds 300 max-age on CSS/JS cache HIT responses
ImageMaxAgeSeconds 1800 max-age on image cache HIT responses

Cache section:

Setting Default Description
Cache.VolumePath /var/cache/pagespeed/volume.dat Path to the Cyclone cache volume file (must be writable)
Cache.VolumeSizeBytes 1073741824 (1 GB) Maximum cache volume size

Worker section:

Setting Default Description
Worker.AutoStart true Launch and manage the worker process on startup. Set false to run no worker.
Worker.SocketPath unset Worker-coordination socket. Leave it unset (the default) for an auto-resolved per-process socket with coordination on. Set to a concrete path to share one socket with an out-of-process worker. Setting it to null or "" disables coordination and logs a startup warning.
Worker.ApiPort 0 (auto, loopback only) Override to expose the worker HTTP API on a fixed port

Console section:

Setting Default Description
Console.MountPath /console URL prefix for the in-app console
Console.RequireHttps false Reject non-HTTPS requests to the console (set true in production)

Options support hot-reload via IOptionsMonitor<PageSpeedOptions>.

Worker IPC

The middleware ↔ worker channel uses Unix domain sockets on Linux and macOS, and Windows Named Pipes on win-x64. Selection is automatic; no configuration required. The console and license endpoints are served on the app port; the worker's HTTP API is bound to 127.0.0.1 on an ephemeral port by default and is not exposed externally unless you set Worker.ApiPort explicitly.

Security

The /console/ admin console and /v1/license/* proxy are served on the same origin as your app. The worker requires Content-Type: application/json and X-Requested-With: XMLHttpRequest on POSTs to /v1/license/*, which blocks cross-origin form posts. It does not protect against same-origin scripts: a third-party script loaded into your app (via XSS or a supply-chain dependency) can drive a license POST such as POST /v1/license/apply to install an attacker-supplied key, because that endpoint is auth-exempt to allow bootstrapping before an API token is configured.

Treat /console/ as an admin surface:

  • Set Console.RequireHttps = true in production.
  • Don't expose /console/ to untrusted networks. Gate it at your reverse proxy, or use Console.MountPath to move the path off a guessable location.
  • Avoid loading untrusted third-party scripts into apps with this middleware enabled.

License

Licensed under the Business Source License 1.1 (BUSL-1.1).

  • Change Date: Four years after the first public release of each version (see the Change Date in the packaged LICENSE file).
  • Change License: Apache License 2.0

After the Change Date, each version becomes available under Apache 2.0. See the LICENSE file in the package for full terms.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on WeAmp.PageSpeed.NativeAssets.Windows:

Package Downloads
WeAmp.PageSpeed

Most ASP.NET Core users want WeAmp.PageSpeed.AspNetCore (the drop-in middleware), which pulls this package as a transitive dependency. This package is the low-level managed binding to the C++23 PageSpeed engine via P/Invoke — install it directly only if you're embedding the optimization library outside of an ASP.NET Core pipeline. Provides HTML transformation, critical CSS extraction, image classification, and high-performance caching.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.42 146 8/10/2026
2.0.41 151 8/8/2026
2.0.40 166 8/1/2026
2.0.39 160 7/23/2026
2.0.38 164 7/17/2026
2.0.37 174 7/12/2026
2.0.36 155 7/5/2026
2.0.35 155 7/3/2026
2.0.34 152 7/3/2026
2.0.33 171 7/1/2026
2.0.32 165 6/24/2026
2.0.30 181 6/21/2026
2.0.29 161 6/18/2026
2.0.28 161 6/16/2026
2.0.27 159 6/15/2026
2.0.26 156 6/15/2026
2.0.25 155 6/14/2026
2.0.24 160 6/14/2026
2.0.23 140 6/12/2026
Loading failed

2.0.41: Security: the published worker and nginx images are rebuilt on an updated base image, picking up the current distribution security updates. This clears several fixable medium-severity vulnerabilities in bundled system libraries. No ModPageSpeed code is affected and no configuration change is needed. Update recommended. Improved: a deferred stylesheet is now requested at normal priority using a standard preload, instead of a low-priority technique that could leave the browser waiting longer before the full stylesheet applied. The deferred stylesheet is also announced in early hints again, so it starts downloading sooner. Pages without JavaScript are unaffected — the no-script fallback is unchanged. The ASP.NET Core middleware emits the same primitive. Note for operators: the async-CSS helper script is served at a content-addressed path that changes with this release. Front-end and worker components must be upgraded together, as with every release. The deferral changes below are not retroactive either: HTML pages already in the cache were written before deferral required measured evidence and can keep serving that way until they revalidate, so purge or reset the cache after upgrading to have every cached page pick up the new behavior immediately. Improved: the optimizer now measures which elements are actually above the fold in a real browser render, per viewport, instead of estimating from the first elements in the document. Pages with substantial markup in the document head — which previously exhausted the estimate before reaching visible content — get more accurate above-the-fold CSS, so stylesheet deferral applies to more pages. Fixed: deferring a page's stylesheet could produce a flash of unstyled content when the inlined above-the-fold CSS did not in fact cover the fold. Deferral now applies only to a page whose above-the-fold appearance has been confirmed unchanged, and that confirmation is tied to the exact stylesheet it was made against — so publishing new styles re-checks before deferring again. Everywhere it does not apply, the stylesheet stays render-blocking and the above-the-fold CSS is still inlined, so those pages keep the inlining benefit and lose only the deferral. A page whose analysis has not completed keeps its stylesheet render-blocking. A confirmation only ever authorizes the above-the-fold CSS it was actually made about. If none can be measured for the specific page being served, that page's stylesheet stays render-blocking whatever its size — a small stylesheet gets no shortcut past the check — and whatever above-the-fold CSS was produced is still inlined. That confirmation now happens. While a page is analyzed, it is rendered twice at each viewport — once with its whole stylesheet, once with only the above-the-fold CSS that would be inlined — and the two are compared. Pages whose above-the-fold appearance is unchanged get their stylesheet deferred again; pages where it is not keep the stylesheet render-blocking and still get the above-the-fold CSS inlined. Deferral therefore applies to pages that have been analyzed, and only while they still serve the stylesheet the check was made against. Three limitations are worth knowing, because they are permanent. The comparison render runs without JavaScript, and without loading images, web fonts or imported stylesheets, so a fold whose appearance depends on any of those is not something the check can see. And the check covers a page template rather than each individual page: pages sharing a template share one confirmation, so a sibling page whose fold needs something the checked page did not can still be deferred. Verify such pages yourself, or turn deferral off for the site. Fixed: a page could have its stylesheet deferred while a content-security policy declared on that same page suppressed the inlined above-the-fold CSS, leaving nothing to paint with until the full stylesheet arrived. Whenever that inlining is refused, the deferral is now withdrawn with it. Changed: on the embedding library's low-level HTML transform entry point, `ps_html_transform_create`, the async-CSS configuration flag is now ignored. That entry point has no view of the page's stylesheets and no browser, so it cannot establish that deferring them is safe, and unsafe deferral is a flash of unstyled content. It always leaves stylesheets render-blocking, and still inlines the above-the-fold CSS it is given. The flag is off by default and the ASP.NET Core middleware does not use this entry point, so this reaches only an embedder that called it and set the flag explicitly; for stylesheet deferral, use the main HTML processing entry point, `ps_html_process`, which gathers the page's stylesheets and gates on them. Fixed: custom-property registrations and related at-rules were omitted from the inlined above-the-fold CSS. On stylesheets that rely on them — modern utility frameworks in particular — properties depending on those registrations computed incorrectly while the full stylesheet was still loading, so borders and similar details rendered wrong for a moment. These declarations are now always retained. Improved: above-the-fold CSS detection now correctly retains rules the browser was observed to use at first paint even when those rules sit inside a cascade layer or a responsive breakpoint, so less of the fold is left unstyled while the full stylesheet loads. Fixed: the JavaScript minifier corrupted valid code when a comment sat between `let` and the name it declares, matching the same fix in mod_pagespeed 1.15. The minifier did not look past the comment when deciding whether `let` starts a declaration, and removed a line break that JavaScript's automatic-semicolon rules needed: `let /*c*/ row` followed by a line starting `+4` was served as the unparseable `let row+4`, breaking the script. The declaration scan now skips comments and the line break is preserved. Fixed: the CSS minifier corrupted custom-property values when the property name contained an escaped character. Custom properties store raw token streams — read back verbatim by var() and getPropertyValue() — so the minifier leaves their values untouched; but the check that recognizes a custom property broke on names with escapes, such as the escaped space in `--\ \>`. Those names are valid per CSS Syntax 3 (an escaped char is a name char), so the value was minified as ordinary CSS and could lose digits: `a{--\ \>:0.}` was served as `a{--\ \>:.}`. The name scan is now escape-aware (a character preceded by an odd-length backslash run is name content, not a boundary), and such properties keep their values byte-for-byte. Fixed: the CSS minifier's decimal optimization still rewrote identifiers that use hexadecimal escapes. 2.0.40 stopped the `0.5` → `.5` shortening from firing inside plain identifiers, but a CSS identifier is built from decoded escapes while the guard read raw bytes: the class selector `.a\35 0.5` — the class named `a50` — was served as `.a5`, silently restyling pages that use escaped identifiers. Escape structure is now decoded before the guard decides, including a hex escape's consumed whitespace terminator and escape content in front of a `-`, so such selectors survive minification unchanged while genuine numbers still minify.
See CHANGELOG.md in the repository for earlier releases.