IIS image optimization in 2026: WebP + AVIF without nginx
Updated 2026-05-20 · We-Amp B.V. · about an 8-minute read
Almost every WebP-and-AVIF tutorial on the web assumes nginx and a
Linux box with a build step. On Microsoft IIS the same problem looks
different. There is no .htaccess, the worker process
runs as a constrained Windows identity, and the obvious
map $http_accept pattern from nginx land has no direct
equivalent. This page is what we tell IIS shops that ask: how do I
actually ship WebP and AVIF on Windows Server, in 2026, without
standing up nginx in front?
Why IIS makes this awkward
Three constraints turn a one-page nginx recipe into a multi-step question on IIS:
- No per-directory configuration override. IIS reads
a single
applicationHost.configplus per-applicationweb.configfiles. There is no.htaccessequivalent that the application owner can drop into the document root, so the typical "convert and serve WebP per directory" recipe becomes a request to the server administrator. - The static-file handler does not negotiate on
Accept. The IIS static handler matches by URL, not by theAcceptheader. Shippinghero.webpnext tohero.jpgmeans the application has to emit a<picture>element with source elements. There is no built-in server-side negotiation. - Application-pool identity gotchas. If you generate
variants at build time and write them into the document root, the
IIS application pool identity (commonly
IIS APPPOOL\<site>or, on older installs,IUSR) needs read access. A pre-build job running as your developer account often produces files the worker process cannot serve until ACLs are sorted out: a class of bug that wastes an afternoon every time.
The combination is why the typical IIS site ships JPEGs and PNGs at source resolution and full quality, loses Largest Contentful Paint by seconds, and never gets the WebP-and-AVIF treatment that comparable nginx sites picked up years ago. There are two practical ways out.
Path 1: classic IIS with mod_pagespeed 1.1
mod_pagespeed 1.1 ships a native IIS module that handles content
negotiation transparently. It is the same code as the IISpeed module, now part
of the 1.1 multi-port release. It plugs into the IIS request pipeline
and rewrites responses before they leave the worker process. The
configuration block lives in applicationHost.config:
<ModPagespeed enabled="true">
<Settings>
<add name="ModPagespeedRewriteLevel" value="CoreFilters" />
<add name="ModPagespeedEnableFilters"
value="recompress_images,convert_jpeg_to_webp,resize_images,insert_image_dimensions,lazyload_images" />
<add name="ModPagespeedFileCachePath"
value="C:\ProgramData\mod_pagespeed\cache" />
<add name="ModPagespeedFileCacheSizeKb" value="1048576" />
</Settings>
</ModPagespeed> Four filters carry the image work:
-
recompress_images: re-encodes JPEG and PNG sources at a sane quality target. The module decides the encoder and quality per image, not per site. -
convert_jpeg_to_webp: generates a WebP variant of each JPEG and serves it to browsers that sendAccept: image/webp. The original JPEG is still available for browsers that do not. -
resize_images: when the rendered dimensions are smaller than the source (a hero image declared at 1200×630 in the HTML but sourced as a 4000×3000 camera JPEG), the module resizes the actual bytes to match. -
insert_image_dimensions: writeswidthandheightattributes onto<img>tags that lack them, removing one of the common sources of Cumulative Layout Shift.
AVIF is a separate consideration. The 1.1 module produces AVIF
variants when its worker is built with AVIF support enabled; the
decision lives at the worker layer, not at a named filter. Sites
upgrading from IISpeed can confirm rewrites are running by hitting
/pagespeed_global_admin and looking at the image
rewrite counters (image_rewrites,
image_webp_rewrites).
On a typical SharePoint or DotNetNuke front page, the visible effect is a hero image that goes from ~600 kB down to under 80 kB on modern browsers, a Largest Contentful Paint improvement of seconds on latency-bound mobile clients.
Path 2: ASP.NET Core with WeAmp.PageSpeed.AspNetCore
If your application is ASP.NET Core (.NET 8 or .NET 9), whether you host it behind IIS via the ASP.NET Core Module or you run Kestrel directly, the IIS-module path is the wrong layer to optimize at. The request never gets to the IIS handler that mod_pagespeed 1.1 plugs into; it flows through Kestrel, the ASP.NET Core pipeline, and back out. The right place to do image work is inside the pipeline, as middleware.
The WeAmp.PageSpeed.AspNetCore NuGet package wraps the
same PageSpeed Automatic optimization library used by mod_pagespeed
1.1, repackaged as middleware. Two lines in
Program.cs:
using WeAmp.PageSpeed.AspNetCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPageSpeed(options =>
{
options.RewriteLevel = "CoreFilters";
options.EnableFilters = new[]
{
"recompress_images",
"convert_jpeg_to_webp",
"resize_images",
"insert_image_dimensions",
"lazyload_images",
};
options.FileCachePath = "/var/cache/pagespeed";
});
var app = builder.Build();
app.UsePageSpeed(); // before app.UseStaticFiles() if you want to optimize static assets
app.UseStaticFiles();
app.MapControllers();
app.Run();
The middleware reads the request's Accept header,
decides whether to serve WebP or AVIF, and emits the appropriate
Vary: Accept response header so downstream caches do not
cross-contaminate variants. The file cache directory should be
writable by the application user. On classic IIS hosting that is
the application pool identity; on a Docker host it is whatever user
the container runs as.
Verifying the install
Whichever path you take, the verification recipe is the same. From a
client off the server itself, request the hero image with an
explicit Accept header and inspect the response:
# Should return image/webp on a modern browser request
curl -sI -H "Accept: image/webp,image/avif,image/*,*/*" \
https://your-site/hero.jpg
# Should return image/jpeg for clients that do not advertise WebP
curl -sI -H "Accept: image/*,*/*" \
https://your-site/hero.jpg
On a successful install, the two requests return different
Content-Type values for the same URL, which means the server is
negotiating. Both responses carry an X-Mod-Pagespeed
header (1.1 module) or an X-PageSpeed header (ASP.NET
Core middleware), so you can confirm the optimization layer
actually saw the request. A Vary: Accept response
header is also present, which is the signal CDNs need in order to
cache the variants separately.
When images are not the bottleneck
A few caveats worth surfacing, because image optimization is the easy headline:
- If your Largest Contentful Paint element is a chunk of text rendered by an above-the-fold JavaScript bundle, no amount of image-format negotiation moves the metric. Run a measurement first; the IIS Core Web Vitals guide covers the workflow.
- If you are already on a CDN with on-the-fly image optimization (Cloudinary, imgproxy, Cloudflare Image Resizing), running another optimizer underneath usually overlaps without benefit. The two paths on this page are for shops doing the image work themselves, either because the CDN cost is wrong at their scale, or because the images are behind authentication and a public CDN is not an option.
-
AVIF encoding is meaningfully more CPU-expensive than WebP. On
high-traffic sites the worker's optimized-variant cache is what
makes AVIF practical: you encode once per source asset, then
serve from cache. If your origin runs on a constrained Windows
Server VM, watch the image rewrite counters at
/pagespeed_global_adminfor a few days and confirm the encoder is not running on every request.
Where to start
New to PageSpeed optimization on IIS: install the module and run it
unlicensed. It fully optimizes out of the box and simply adds an
X-PageSpeed-Warn: unlicensed header, so you can measure
both paths against your own pages before you buy. When you are ready
for production, buy a
license — a commercial license is required for production use,
billed immediately on purchase via FastSpring. The
mod_pagespeed 1.1 getting-started
guide covers the classic-IIS install; the
ASP.NET Core image-optimization walkthrough
on modpagespeed.com goes deeper on the middleware path with a worked
example. Existing IISpeed customers get the 1.1 IIS module via the
free IISpeed
license transfer.
Ship WebP and AVIF on IIS
Install the module and run it unlicensed to measure both the IIS
module and the ASP.NET Core middleware against your own hero images
— it fully optimizes and just adds an
X-PageSpeed-Warn: unlicensed header. A commercial
license is required for production use.
Related
- IISpeed transition overview — what changed, how the license transfers, and where the optimization library lives now.
- IIS Core Web Vitals in 2026 — measurement workflow and the filters that move each metric.
- ASP.NET Core on IIS: the WeAmp.PageSpeed shape — the architectural truth about ASP.NET Core behind IIS and where the middleware fits.
- ASP.NET Core image optimization in C# — a worked example of the middleware path on modpagespeed.com.
- IISpeed successor: mod_pagespeed 1.1 for IIS — the canonical successor page.