0

** UPDATE Jan 27, 2021 ** I have been working with Microsoft on this issue and we did a request trace and reviewed the FREB log and confirmed that the code is outputting the correct headers but in the END_REQUEST handler they are replaced with cache-control private. After building several virtual machines from scratch we learned that out of the box, this problem doesn't happen. However when we install the latest version of STACKIFY AGENT (4.29.29) once we put the agent on this behavior happens. Older versions of Stackify agent (RETRACE profiler and APM) don't do this, but so far, this is irreversible: once we install 4.29.29 of Stackify, uninstalling or installing older versions doesn't undo this issue, the environment is somehow permanently modified.

STACKIFY responded with a solution which works (but suggests something is left behind after uninstall): Set the environment variable STACKIFY_ENABLERUM = false .. we tried this and IIS returned our correct header without replacing it with cache-control: private.


I want to use CloudFront CDN to cache traffic to my API, so that requests are offloaded to the content delivery network.

I'm trying to set the Cache-Control header to return: Cache-Control: "public, max-age=10". When I run my code in Visual Studio 2019 to debug it, I get the correct header. However, when I deploy it to IIS, I always get back:

Cache-Control: private

I am running Windows Server 2019 with IIS version 10.0.17763.1. I am using ASP.NET MVC with Web API 2 to operate a REST API.

In my code I created this attribute:

Code:

 public class CacheControlAttribute : System.Web.Http.Filters.ActionFilterAttribute
    {
        public int MaxAge { get; set; }

        public CacheControlAttribute()
        {
            MaxAge = 0;
        }

        public override void OnActionExecuted(HttpActionExecutedContext context)
        {
            // Don't set the cache header if there was an error or if there was no content in the response
            if (context.Response != null && context.Response.IsSuccessStatusCode)
            {
                context.Response.Headers.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue
                {
                    Private = false,
                    Public = true,
                    MaxAge = TimeSpan.FromSeconds(MaxAge)
                };                

            }

            base.OnActionExecuted(context);
        }
    }

I then decorate my method with this attribute:

[CacheControl(MaxAge = 10)]

I read several articles on StackOverflow but was not able to solve this issue. I also tried adding this to web.config:

It did not help.

I have this working successfully on one IIS server, same version of IIS but older version of my code. I'm having the problem setting it up on a new IIS server and I think its an IIS configuration issue but I am not certain. Maybe its something in my web.config.

Thank you for any help with this.

DavidJBerman
  • 945
  • 1
  • 10
  • 17
  • ** DEPLOYED SAME EXACT CODEBASE TO DIFFERENT IIS SERVER (same version) and cache control header is returned correctly. Is there some IIS10 configuration setting that needs to be updated? – DavidJBerman Jan 08 '21 at 21:30

2 Answers2

1

Cache-control is divided into request and response directives. I guess you are talking about the cache-control in the response, because the request cache-control cannot be set in IIS.

For security reasons, IIS will set cache-control to private by default, so you will encounter this problem. This is default behaviour for .NET when there's no output cache used for a request (and you have output cache enabled). If you set the sendCacheControlHeader to false in web.config, you will not get the Cache-Control: private header.

So an easy way is set sendCacheControlHeader false and add cache-control will remove private. You also don't need to custom cache-control filter in MVC.

<system.web>
   <httpRuntime sendCacheControlHeader="false" /> 
</system.web>

<httpProtocol>
 <customHeaders>
   <remove name="X-Powered-By" />
    <add name="Cache-Control" value="max-age=30,public" />
 </customHeaders>
</httpProtocol>

Another idea is to add cached output for each action or controller. .NET has a defined filter to control maxage, you don't need to define it yourself. You can use [OutputCache(Duration = 0)]

OutputCache

Bruce Zhang
  • 2,880
  • 1
  • 5
  • 11
  • Thank you Bruce. I am using the filter so I can set different cache durations for different APi requests - some 1 second some 10 seconds and some no cache depending on what the request is doing. – DavidJBerman Dec 31 '20 at 10:02
  • Duration of OutputCache can set different time of cache, 1,10 or 0. – Bruce Zhang Jan 01 '21 at 04:12
  • Bruce, I want to offload requests to the CDN, not use IIS OutputCache. I tried adding the sendCacheControlHeader="false" and I'm still seeing the Cache-Control response header set to private. Do you know how I can turn off OutputCache entirely? I tried adding the [System.Web.Mvc.OutputCache(Duration=0)] property on my WebApi Controller Method but that didn't solve this issue either. – DavidJBerman Jan 05 '21 at 21:57
  • MARKING THIS AS ANSWER because it does answer my original question. It turns out that my cache header was being replaced by a defect in STACKIFY. You need to use an environment variable to disable stackify RUM or it will do this to you even if you uninstall. – DavidJBerman Feb 02 '21 at 13:18
0

You need to implement your own on top of the existing output cache like so

public static class MyCustomCacheConfig
{
    public static int Duration = 600; //whatever time you want
}

public class CdnCacheCacheAttribute : OutputCacheAttribute
{
    public CdnCacheCacheAttribute()
    {
        this.Duration = MyCustomCacheConfig.Duration;
    }
}

[CdnCacheCacheAttribute(Duration = 100, VaryByParam = "none")]
public ActionResult YourActions()
{
    return View();
}

VaryByParm in output cache from Microsoft.. which controls the HttpCaching

// Get the current VaryByParam.
String varyByParamValue =  outputCacheProfile.VaryByParam;

// Set the VaryByParam.outputCacheProfile.VaryByParam = string.Empty;
Transformer
  • 6,963
  • 2
  • 26
  • 52
  • Thank you but I am using WEB API v2 and not VIEWS. I made a cache directive and when my application runs on IIS Express with Visual Studio, and in production on one IIS 10 Web Server, the correct headers are returned. On another IIS server they are overridden with cache-control private. – DavidJBerman Jan 24 '21 at 20:06