V13 Image Cache Control

In v13 my images are loading with a cache-control max-age=604800 (aka 7 days). I’m getting dinged for it on the Google Lighthouse performance score. So I wanted to up the max-age.

I found some settings in appsettings that I thought would do the trick, but it doesn’t seem that these are working. Do I have these settings implemented correctly?

"Umbraco": {
    "CMS": {
        "Global": {
            ...OTHER STUFF HERE...
            "ImagingCacheSettings": {
                "BrowserMaxAge": "365.00:00:00",
                "CacheMaxAge": "365.00:00:00",
                "CacheFolderDepth": 8,
                "CacheHashLength": 12,
                "CacheFolder": "~/umbraco/Data/TEMP/MediaCache"
            },
            "ImagingResizeSettings": {
                "MaxWidth": 1920,
                "MaxHeight": 1080
            }
            ...OTHER STUFF HERE...
        }
    }
}

Where else should I be looking?

These settings are for the media cache on the disk, it has nothing to do with the header :slight_smile:

Try uploading a new media item are the cache headers correct?

At least in Azure blob storage all the cache information is also stored with the cached item.

So you will need to clear down the cache directory

@MattW I uploaded a new image still 7 day max-age.

@LuukPeters the default for ImagingCacheSettings > BrowserMaxAge is 7 days and that’s the same max-age I’m seeing on all of my images rendered by Umbraco.

I tried this setting in web.config

<staticContent>
        <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="30.00:00:00" />
      </staticContent>

Still showing max-age of 7 days on all images rendered by Umbraco.

I got it working with a URL rewrite rule in web.config

<rewrite>
<outboundRules>
          <rule name="Add Cache Control for Images" preCondition="IsImage">
            <match serverVariable="RESPONSE_Cache-Control" pattern=".*" />
            <action type="Rewrite" value="max-age=2592000" />
          </rule>
          <preConditions>
            <preCondition name="IsImage">
              <add input="{RESPONSE_Content-Type}" pattern="^image/(jpeg|png|gif|webp|svg\+xml)" />
            </preCondition>
          </preConditions>
        </outboundRules>
      </rewrite>

if you don’t want to do in outboundrules.. as can’t always use them… (slight update from
Response Caching | Umbraco CMS)

But for imagesharp cache, you should follow the app setting mentioned here too.
Umbraco:CMS:Imaging:Cache:BrowserMaxAge

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Headers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Extensions;
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;

namespace Umbraco.Docs.Samples.Web.Tutorials;

public class ConfigureStaticFileOptions : IConfigureOptions<StaticFileOptions>
{
    // These are the starting segments of the paths we want to cache
    private static readonly HashSet<string> _cachedFilePaths = new(StringComparer.OrdinalIgnoreCase)
    {
        "/css","/styles","/scripts","/js","/images","/fonts", "/app_plugins"
    };

    private readonly string _backOfficePath;
    private readonly bool _isDevelopmentEnv;

    public ConfigureStaticFileOptions(IOptions<GlobalSettings> globalSettings, IHostingEnvironment hostingEnvironment, IWebHostEnvironment env)
    {
        _backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment);
        _isDevelopmentEnv = env.IsDevelopment();
    }

    public void Configure(StaticFileOptions options)
        => options.OnPrepareResponse = ctx =>
        {
            // if we are in development
            if (_isDevelopmentEnv)
            {
                return;
            }
            // Exclude Umbraco backoffice assets
            if (ctx.Context.Request.Path.StartsWithSegments(_backOfficePath))
            {
                return;
            }

            if (_cachedFilePaths.Any(s => ctx.Context.Request.Path.StartsWithSegments(s, StringComparison.OrdinalIgnoreCase)))
            {
                ResponseHeaders headers = ctx.Context.Response.GetTypedHeaders();

                // Update or set Cache-Control header
                CacheControlHeaderValue cacheControl = headers.CacheControl ?? new CacheControlHeaderValue();
                cacheControl.Public = true;
                cacheControl.MaxAge = TimeSpan.FromDays(30);                
                headers.CacheControl = cacheControl;
            }
        };

    public class ResponseCacheingComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
            => builder.Services.AddTransient<IConfigureOptions<StaticFileOptions>, ConfigureStaticFileOptions>();
    }
}