Configuration and Pipeline
ImageSharp.Web is assembled through AddImageSharp() and the returned IImageSharpBuilder. Most applications never need to replace every piece, but it helps to know what is there so you can change the correct layer without over-customizing the whole pipeline.
Request Pipeline Model
For a processed image request, the middleware does four separate jobs:
- parse and normalize the requested commands;
- resolve the source image through a provider;
- look up or write the processed result through a cache;
- decode, process, and encode the image when the cache does not already contain a valid result.
Those layers are intentionally separate. Providers are source-of-truth readers, caches store derived output, processors transform the decoded image, and encoders decide the response format. Most customization should replace one layer at a time instead of rebuilding the whole middleware configuration.
What AddImageSharp() Registers
The default registration wires up:
QueryCollectionRequestParserfor query-string command parsing.PhysicalFileSystemCachefor processed-output storage.UriRelativeLowerInvariantCacheKeyandSHA256CacheHashfor cache naming.PhysicalFileSystemProviderfor source image resolution.- The built-in resize, format, quality, background-color, and auto-orient processors.
- The built-in command converters for numbers, booleans, strings, arrays, lists, colors, and enums.
That gives you a fully working middleware out of the box, but every one of those pieces can be swapped or extended.
Configure Middleware Options
ImageSharpMiddlewareOptions controls the shared middleware behavior:
Configurationis the underlying ImageSharpConfiguration.- By default, that
Configurationis not rawConfiguration.Default; ImageSharp.Web installs web-oriented JPEG, PNG, and WebP encoders into it. OnParseCommandsAsyncdefaults to a callback that injectsautoorient=truewhen the request does not already containautoorient.MemoryStreamManagercontrols pooled response streams.UseInvariantParsingCulturecontrols whether command parsing is culture-invariant.BrowserMaxAge,CacheMaxAge, andCacheHashLengthcontrol cache behavior.
using SixLabors.ImageSharp.Web;
using SixLabors.ImageSharp.Web.Caching;
using SixLabors.ImageSharp.Web.Providers;
builder.Services.AddImageSharp(options =>
{
options.UseInvariantParsingCulture = true;
options.BrowserMaxAge = TimeSpan.FromDays(7);
options.CacheMaxAge = TimeSpan.FromDays(30);
options.CacheHashLength = 16;
})
.Configure<PhysicalFileSystemProviderOptions>(options =>
{
options.ProviderRootPath = "assets";
options.ProcessingBehavior = ProcessingBehavior.CommandOnly;
})
.Configure<PhysicalFileSystemCacheOptions>(options =>
{
options.CacheRootPath = "cache";
options.CacheFolder = "imagesharp";
options.CacheFolderDepth = 8;
});
If you do not need to change format registrations or encoder defaults, leave ImageSharpMiddlewareOptions.Configuration alone. Replacing it opts you out of the middleware's built-in web defaults.
Likewise, if you do not need custom command augmentation, leave OnParseCommandsAsync alone. Replacing it opts you out of the default EXIF-normalization behavior unless you chain the existing callback yourself.
Default Orientation Behavior
The default OnParseCommandsAsync callback inserts autoorient=true when the request does not already specify autoorient.
That makes EXIF normalization part of the default middleware behavior rather than an opt-in query-string feature. The main reason is web delivery: some browsers still ignore EXIF orientation in formats such as WebP, so relying on the encoded metadata alone does not produce consistent display results.
Two details matter in practice:
autoorient=falsestill disables the behavior for that request because the middleware only inserts the command when it is absent.- Replacing
OnParseCommandsAsyncwith your own delegate removes the built-in insertion unless you invoke the previous delegate.
With the out-of-the-box local filesystem setup, that also means commandless image URLs are usually processed and cached instead of falling through to static files unchanged.
Default Encoder and ICC Behavior
The default middleware Configuration is a cloned ImageSharp configuration with web-oriented encoder registrations:
JpegEncoderusesQuality = 75,Progressive = true,Interleaved = true, andColorType = YCbCrRatio420.PngEncoderusesCompressionLevel = BestCompressionandFilterMethod = Adaptive.WebpEncoderusesQuality = 75andMethod = BestQuality.
Those registrations are used whenever the middleware saves processed output in JPEG, PNG, or WebP format, whether the format came from the source image or from the format command.
If OnBeforeLoadAsync returns null, the middleware also creates fallback DecoderOptions for you. The ICC-profile behavior depends on whether you kept the default configuration:
- With the default middleware configuration,
DecoderOptions.ColorProfileHandlingisColorProfileHandling.Convert. - If you replace
options.Configuration, the fallback changes toColorProfileHandling.Compact.
Compact only removes canonical sRGB ICC profile data. It does not convert non-sRGB source images. That distinction matters most when you transcode or resize JPEGs that arrive with CMYK or other non-sRGB profiles.
Customize Encoders Without Losing ICC Conversion
If you want your own encoder registrations but still want the middleware to decode with ColorProfileHandling.Convert, clone the current configuration, replace the encoders you care about, then return explicit decoder options:
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Web;
builder.Services.AddImageSharp(options =>
{
Configuration configuration = options.Configuration.Clone();
configuration.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder
{
Quality = 82,
Progressive = true,
Interleaved = true,
ColorType = JpegColorType.YCbCrRatio420
});
options.Configuration = configuration;
options.OnBeforeLoadAsync = (_, _) => Task.FromResult<DecoderOptions?>(new()
{
Configuration = configuration,
ColorProfileHandling = ColorProfileHandling.Convert
});
});
Use this pattern when you want to keep ImageSharp.Web's ICC-conversion behavior but need different encoder quality, chroma subsampling, format registrations, allocator settings, or other base ImageSharp customization.
Change Individual Pipeline Pieces
The builder methods let you replace only the layer you actually need to change:
SetRequestParser<TParser>()replaces the request parser.SetCache<TCache>()replaces the backend cache.SetCacheKey<TCacheKey>()andSetCacheHash<TCacheHash>()change cache naming.AddProvider<TProvider>(),InsertProvider<TProvider>(),RemoveProvider<TProvider>(), andClearProviders()manage source providers.AddProcessor<TProcessor>(),RemoveProcessor<TProcessor>(), andClearProcessors()manage the processing command set.AddConverter<TConverter>(),RemoveConverter<TConverter>(), andClearConverters()manage typed command parsing.Configure<TOptions>(...)binds or mutates option objects for any registered provider, cache, or parser.
For example, if you want to keep the default middleware but remove format conversion:
using SixLabors.ImageSharp.Web;
using SixLabors.ImageSharp.Web.Processors;
builder.Services.AddImageSharp()
.RemoveProcessor<FormatWebProcessor>();
Use Presets Instead of Free-Form Query Strings
PresetOnlyQueryCollectionRequestParser is the built-in alternative to the normal query parser. Instead of reading every query-string command, it reads a single preset key and expands that to a predefined command set.
using SixLabors.ImageSharp.Web;
using SixLabors.ImageSharp.Web.Commands;
builder.Services.AddImageSharp()
.SetRequestParser<PresetOnlyQueryCollectionRequestParser>()
.Configure<PresetOnlyQueryCollectionRequestParserOptions>(options =>
{
options.Presets["thumb"] = "width=160&height=160&rmode=crop";
options.Presets["card"] = "width=640&height=360&rmode=crop&format=webp&quality=75";
});
That turns requests like /images/photo.jpg?preset=thumb into a controlled, named command set without exposing arbitrary query-string processing.
Middleware Callbacks
ImageSharpMiddlewareOptions also exposes targeted callbacks for app-specific customization:
OnParseCommandsAsyncruns after a provider has matched the request and after the command set has been sanitized, but before the source image is resolved.OnBeforeLoadAsynccan return customDecoderOptionsbefore the source image is decoded. If it returnsnull, the middleware supplies defaults based on the currentConfiguration.OnBeforeSaveAsynccan adjust theFormattedImageafter processing but before encoding.OnProcessedAsyncreceives anImageProcessingContextafter encoding but before the result is cached.OnPrepareResponseAsyncruns after status code and headers are set but before the body is written.
using SixLabors.ImageSharp.Web;
using SixLabors.ImageSharp.Web.Middleware;
builder.Services.AddImageSharp(options =>
{
Func<ImageCommandContext, Task> defaultParse = options.OnParseCommandsAsync;
options.OnParseCommandsAsync = async context =>
{
await defaultParse(context);
if (!context.Commands.Contains("format"))
{
context.Commands["format"] = "webp";
}
};
options.OnPrepareResponseAsync = context =>
{
context.Response.Headers["X-ImageSharp"] = "true";
return Task.CompletedTask;
};
});
These callbacks are often the right tool when you need small workflow adjustments without inventing a custom provider, parser, or processor. If you override OnParseCommandsAsync, preserve the existing delegate unless you intentionally want to remove the middleware's default autoorient=true insertion.
Related Topics
Practical Guidance
Most ImageSharp.Web configuration changes affect one stage of the request pipeline: command parsing, source resolution, image processing, encoding, caching, or response behavior. Change the stage that owns the behavior you need, and keep the others boring. For example, a provider should resolve source images; it should not also reinterpret resize commands. A parser should shape the command collection; it should not open streams.
Be careful when replacing callbacks. The default OnParseCommandsAsync inserts autoorient=true when the request does not specify orientation behavior, so replacing it without preserving the existing delegate also changes default output. That can be correct for passthrough scenarios, but it should be a deliberate compatibility decision.
For public URLs, presets are often a better product surface than arbitrary query strings. They limit the transformation vocabulary, simplify HMAC signing, reduce cache explosion, and make generated variants easier to reason about. When you do allow free-form commands, keep the middleware ImageSharp configuration aligned with your encoder and ICC expectations so a URL means the same thing across deployments.