Disable caching PDFs/Word/Excel

Hi all,

We have a very specific business requirement where updated PDFs/Word/Excel files need to be immediately available on the website. On older versions of Umbraco I knew how to disable caching for /media/ path but I am not managing to find a good solution for version 13 (and eventually 17). Ideally caching would be disabled only for PDFs/Word/Excel and not for images. Would anyone know where I should look?

Thank you!
Genc

I have something like this for static assets.. not sure if it can be expanded to /media/ paths..
Which looks like it started life here Response Caching | CMS | Umbraco Documentation

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.Cms.Core.Hosting;
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 = hostingEnvironment.GetBackOfficePath(); ;
        _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>();
    }
}

looks like AI thinks this is the correct place to start..

Path Type Served by
/css/site.css Static asset StaticFileOptions / wwwroot
/scripts/app.js Static asset StaticFileOptions / wwwroot
/media/1234/doc.pdf Media asset Umbraco media pipeline via StaticFileOptions
/media/1234/image.jpg Media asset ImageSharp (images only)

Both actually go through StaticFileOptions — which is why the IConfigureOptions<StaticFileOptions> approach works for both. The key difference is that for images specifically, ImageSharp intercepts before static files middleware kicks in, which is why you need the backoffice path guard.

could also use
var mediaService = scope.ServiceProvider.GetRequiredService<IMediaService>(); var media = mediaService.GetMediaByPath(path);
but hits the DB, so you’d probably want to cache…

if you wanted to dymically override from media node property, or parentfolder…

Thank you Mike, this looks great. The solution I have found is this, but I am hesitant to deploy it, don’t know what negative effects it might have. What do you think?

app.Use(async (context, next) =>
{
    context.Response.Headers.Add(“X-Frame-Options”, “SAMEORIGIN”);

    var path = context.Request.Path.Value ?? string.Empty;
    var ext = Path.GetExtension(path).ToLowerInvariant();

    if (ext == ".pdf" || ext == ".doc" || ext == ".docx" || ext == ".xls" || ext == ".xlsx")
    {
        context.Response.Headers["Cache-Control"] = "no-cache, no-store, must-revalidate";
        context.Response.Headers["Pragma"] = "no-cache";
        context.Response.Headers["Expires"] = "0";
    }

    await next();
});

Think it’s doing the same but in middleware, rather than hooking into the staticfileoptions that umbraco exposes and you’ve got magic strings (header names) :wink:

You might have to watch where in the pipeline it sits incase umbraco overrides etc…

Ye something tells me it might not be the best approach. lol magic strings. Thank you for your solution!

No problem, though only really pointing you to the umbraco docs I used.. :slight_smile:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.