Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page in preview mode is cached if route has caching #10989

Closed
pguilmettenmedia opened this issue Aug 30, 2021 · 2 comments
Closed

Page in preview mode is cached if route has caching #10989

pguilmettenmedia opened this issue Aug 30, 2021 · 2 comments
Labels

Comments

@pguilmettenmedia
Copy link
Contributor

Which exact Umbraco version are you using? For example: 8.13.1 - don't just write v8

8.8.0 originally but I have the same problem on 8.16.0

Bug summary

When Preview mode is enabled for the current user and they browse a published page that has a custom controller with caching enabled (using the built-in OutputCache attribute like [OutputCache(Duration = 300, Location = OutputCacheLocation.Any, VaryByParam = "none")]), the preview page is cached by our caching service that "front" our website (Azure Front Door in my case) and is shown to every user until the cache expiration.

Specifics

The page in preview mode is shown to every user who visits the website for 5 minutes wether or not they're logged in because it is cached (the 300 seconds specified in the OutputCache specified above) by a caching service (Azure Front Door) in front of the website because of the Cache-control: public, max-age: 300 header.

The problem don't happen If it's a dedicated preview URL (ex: http://localhost:8160/umbraco/preview/?id=1113), it is not a problem because the OutputCache attribute is not set on that route, so the Cache-control header is equal to no-cache. The problem is only when you go on a published page URL and the preview mode is enabled (ex: http://localhost:8160/products/tattoo/)

Example of published page in preview mode with the preview badge (url: http://localhost:8160/products/tattoo/):
image

Here's an example:

All three routes is routed by the customer controller I made for them (ProductController.cs):

using System.Web.Mvc;
using System.Web.UI;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;

namespace Umbraco.Web.UI.Controllers.Mvc
{
    public class ProductController : RenderMvcController
    {
        [OutputCache(Duration = 300, Location = OutputCacheLocation.Any, VaryByParam = "none")]
        public override ActionResult Index(ContentModel model)
        {
            return CurrentTemplate(model);
        }
    }
}

Steps to reproduce

  1. Add caching to pages. (look for my example above to add caching to product pages)
  2. Login into the backoffice
  3. Click on any published product page available
  4. Click Save and preview on that published product page
  5. On the previewed product page (example: http://localhost:8160/umbraco/preview/?id=1113), click Open in browser
  6. Go on any published product page URL on the website
  7. You should see the Preview mode badge at the bottom of the page
  8. Open the same page in another browser or in private mode
  9. You should also see the Preview mode badge at the bottom of the page

Expected result / actual result

We should not see the Preview mode badge at the bottom of the page when it's a published page URL on the second browser/private tab (ex: http://localhost:8160/1113 and http://localhost:8160/products/biker-jacket/).

I'm not sure what I should do in this situation. If we're previewing with the Open in browser option, I believe it should also set the Cache-control header with the value no-cache, no-store, must-revalidate even if we have a custom controller with the OutputCache attribute like this URL: http://localhost:8160/umbraco/preview/?id=1113. The only caveat will be that if someone wants to preview a published page with its public URL (ex: http://localhost:8160/products/biker-jacket/) and someone browsed that page 1-2 minutes before, the logged in user won't be able to preview the page because the cached one will be returned instead.

I've thought of reading the UMB_PREVIEW cookie to make sure its value is equal to preview on the caching server, but it's not an option with Azure Front Door unfortunately. But if we can at least make sure the public page is never cached when it's in preview mode, it would be acceptable.

@pguilmettenmedia pguilmettenmedia changed the title Page in preview mode is cached if route has caching enabled on the page Page in preview mode is cached if route has caching enabled Aug 30, 2021
@pguilmettenmedia pguilmettenmedia changed the title Page in preview mode is cached if route has caching enabled Page in preview mode is cached if route has caching Aug 30, 2021
@nul800sebastiaan
Copy link
Member

Looks like reading the cookie is exactly what you should be doing. Have a read here: jbreuer/Hybrid-Framework-Best-Practices#2

And have a look at Jeavon's link there, that should help get you further along. Not sure if there's a better / newer way to do MvcDonutCaching these days.

@pguilmettenmedia
Copy link
Contributor Author

Thank you for the help @nul800sebastiaan! In my case, I was using the built-in OutputCacheAttribute to cache the whole page, so I ended up creating an attribute that extend this attribute and adding the custom logic from Jeavon's link to look for a value in the UMB_PREVIEW cookie. If there's a value, I set the Cache-control header to no-cache, no-store, must-revalidate to make sure it's never cached:

public class PageOutputCacheAttribute : OutputCacheAttribute
{
  public PageOutputCacheAttribute(string varyByParam = "none")
  {
    CacheProfile = "Default";
    VaryByParam = varyByParam;
  }

  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    if (string.IsNullOrWhiteSpace(filterContext.HttpContext.Request.Cookies["UMB_PREVIEW"]?.Value))
    {
      base.OnActionExecuting(filterContext);
    }
    else
    {
      HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
      cache.SetCacheability(HttpCacheability.NoCache);
      cache.SetNoStore();
      cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
    }
  }
}

So now, when someone in preview mode request a published page, it's not cached. If someone who's not in preview mode request the published page, it will be added in the cache and the following requests whether or not you're in preview mode will be the published version and not the one in preview mode which is what I want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants