Working with Save-Data header in ASP.NET Core

Posted by Anuraj on Sunday, February 3, 2019 Reading time :2 minutes


This post is about working with Save-Data header in ASP.NET Core. The Save-Data client hint request header available in Chrome, Opera, and Yandex browsers lets developers deliver lighter, faster applications to users who opt-in to data saving mode in their browser.

One fairly straightforward technique is to let the browser help, using the Save-Data request header. By identifying this header, a web page can customize and deliver an optimized user experience to cost- and performance-constrained users.

For this post, a middleware is getting implemented, which is looking for Image files(JPG), and it save-data header exists, it is compressing the images and delivering it.

public class SaveDataMiddleware
    private readonly RequestDelegate _next;
    private readonly IHostingEnvironment _hostingEnvironment;
    private static readonly string[] suffixes = new string[] {

    public SaveDataMiddleware(RequestDelegate next, IHostingEnvironment hostingEnvironment)
        _next = next;
        _hostingEnvironment = hostingEnvironment;

    public async Task Invoke(HttpContext httpContext)
        var path = httpContext.Request.Path;
        if (!IsImagePath(path))
            await _next.Invoke(httpContext);

        var isSaveDataEnabled = false;
        if (httpContext.Request.Headers.TryGetValue("save-data", out StringValues saveDataHeaders))
            isSaveDataEnabled = saveDataHeaders.Count == 1 && 
                saveDataHeaders[0].Equals("on", StringComparison.OrdinalIgnoreCase);

        if (isSaveDataEnabled)
            var imagePath = Path.Combine(_hostingEnvironment.WebRootPath, 
                path.Value.Replace('/', Path.DirectorySeparatorChar).TrimStart(Path.DirectorySeparatorChar));
            using (var image = Image.FromFile(imagePath))
                using (var lowQualityImage = new Bitmap(image.Width, image.Height))
                    using (var graphics = Graphics.FromImage(lowQualityImage))
                        graphics.InterpolationMode = InterpolationMode.Low;
                        graphics.SmoothingMode = SmoothingMode.HighSpeed;
                        graphics.CompositingQuality = CompositingQuality.HighSpeed;
                        graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed;
                        var imageRectangle = new Rectangle(0, 0, image.Width, image.Height);
                        graphics.DrawImage(image, imageRectangle);
                        using (var memoryStream = new MemoryStream())
                            lowQualityImage.Save(memoryStream, image.RawFormat);
                            httpContext.Response.ContentLength = memoryStream.Length;
                            await httpContext.Response.Body.WriteAsync(memoryStream.ToArray(), 0, (int)memoryStream.Length);

            await _next(httpContext);
    private bool IsImagePath(PathString path)
        if (path == null || !path.HasValue)
            return false;

        return suffixes.Any(x => x == Path.GetExtension(path.Value));

And next you can configure extension method to add it to http pipeline.

public static class SaveDataMiddlewareExtensions
    public static IApplicationBuilder UseSaveDataMiddleware(this IApplicationBuilder builder)
        return builder.UseMiddleware<SaveDataMiddleware>();

You need to add this middleware before StaticFilesMiddleware, otherwise it won’t work properly.

And you can verify this implementation using Data Saver chrome extension.

Chrome Data-Saver extension

If Data-Saver mode is enabled, browser will send a HTTP Request header.

Data-Saver header

The page display 5 images. And here is the network tab, without save-data mode.

Chrome network tab - without save-data header

And here is Network tab, after enabling the save-data header.

Chrome network tab - with save-data header

In this post, I am only handling JPG images, you can also exclude stylesheets and fonts etc, if it is not critical for the page. You can get more details about save-data header and various implementations here - Delivering Fast and Light Applications with Save-Data

Happy Programming :)

What do you think? I would like to hear your thoughts, suggestions, and questions in the comments section below.

Similar Posts

Did you like this article? Share it with your friends

Facebook Twitter Google+ LinkedIn Reddit StumbleUpon

BMC logoBuy me a coffee

Copyright © 2019 - Anuraj P. Blog content licensed under the Creative Commons CC BY 2.5 | Unless otherwise stated or granted, code samples licensed under the MIT license. This is a personal blog. The opinions expressed here represent my own and not those of my employer. Hosted with ❤ by GitHub