ASP.NET Core: Adding cache static file using HTTP headers
Tags: dotnet, dotnetcore, csharp
Published: 2021-10-30 09:19:53

In this article, I will explain and guide how to add a Cache-Control header to static files in Asp.net Core. This is a technology help caching static files in the browser of the end-user and from the second request it's no need to request from the server, it can get from the cache disk of the browser, this helps increase the time loading website.
   <br />
   * Cache Type: Cache client, CDN, caching in memory browser of end-user....<br />
   * Data Cache: Only static files, such as images, css, js.<br />
   * Useful case: From second revisit page.<br />
   Okay, to test static files cache-control using <strong>HTTP Headers in Asp.net Core</strong> I will create an example with 50 big images to see how the loading page time from the second request.<br />
   <h2 id="1_Setup_environment_amp;_create_the_project">1. Setup environment &amp; create the project</h2>
   - Visual studio 2019<br />
   - Asp.Net Core 2.0, 2.1, 2.2, 3.0.3.1. In this article, I’m using version 3.1 to make an example.<br />
   Okay, now we open vs2019 and create a web application project to make an example now.<br />
   Step 1. Open vs2019 -&gt; click <strong>Create a new project</strong> option on the right side.<br />
   Step 2. Select <strong>ASP.NET Core Web Application</strong> project type and click <strong>Next</strong> button.<br />
   Step 3. Enter the project name and click <strong>Create</strong> button.<br />
   Step 4. Select the project type is <strong>Web Application</strong> and click <strong>Create</strong> button.<br />
   <h2 id="2_Test_performance_load_default">2. Test performance load default</h2>
   Now I will display 50 big images on a web page and test the performance of it from the first time and second time.&nbsp;<br />
   This is my web page with 50 images:<br />
   <p style="text-align:center">
      <elem><img alt="ASP.NET Core: Adding cache static file using HTTP headers" data-height="438" data-width="666" class=" ezlazyloaded" src="https://quizdeveloper.com/ezoimgfmt/img.quizdeveloper.com/Images/2020926741930ASP-NET-Core-Adding-cache-static-file-using-HTTP-headers.jpg?ezimgfmt=rs:666x438/rscb1/ng:webp/ngcb1" ezimgfmt="rs rscb1 src ng ngcb1" data-ezsrc="https://quizdeveloper.com/ezoimgfmt/img.quizdeveloper.com/Images/2020926741930ASP-NET-Core-Adding-cache-static-file-using-HTTP-headers.jpg?ezimgfmt=rs:666x438/rscb1/ng:webp/ngcb1" ezoid="0.4473608825148243"></elem>
   <br />
   By default, <strong>Cache-control</strong> can not&nbsp;add to the header response of a static file. When you finish the created project you can open <code class="short-code">StartUp.cs</code> file and inside of <code class="short-code">Configure()</code> method&nbsp; and you will see middleware <code class="short-code">UseStaticFiles()</code> add default to your project.&nbsp;<br />
   <div class="ads-code">&nbsp;</div>
   - <code class="short-code">UseStaticFiles()</code>: Help enables static file serving for the current request path. By default, the root static folder is <code class="short-code">wwwroot</code>. So all static files you need put inside this folder.<br />
   <div class="format-code">
      <pre class=" language-csharp" tabindex="0"> <code class=" language-csharp"><span class="token keyword keyword-public">public</span> <span class="token return-type class-name"><span class="token keyword keyword-void">void</span></span> <span class="token function">Configure</span><span class="token punctuation">(</span><span class="token class-name">IApplicationBuilder</span> app<span class="token punctuation">,</span> <span class="token class-name">IWebHostEnvironment</span> env<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  ……<span class="token range operator">..</span>
  app<span class="token punctuation">.</span><span class="token function">UseStaticFiles</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  ……<span class="token range operator">..</span>
<span class="token punctuation">}</span></code> </pre>
   </div>
   Now, we run applications and check web page time load with developer tools.<br />
   <p style="text-align:center">
      <elem><img alt="ASP.NET Core: Adding cache static file using HTTP headers" data-height="338" data-width="689" class=" ezlazyloaded" src="https://quizdeveloper.com/ezoimgfmt/img.quizdeveloper.com/Images/2020926736479ASP-NET-Core-Adding-cache-static-file-using-HTTP-headers_1.jpg?ezimgfmt=rs:689x338/rscb1/ng:webp/ngcb1" ezimgfmt="rs rscb1 src ng ngcb1" data-ezsrc="https://quizdeveloper.com/ezoimgfmt/img.quizdeveloper.com/Images/2020926736479ASP-NET-Core-Adding-cache-static-file-using-HTTP-headers_1.jpg?ezimgfmt=rs:689x338/rscb1/ng:webp/ngcb1" ezoid="0.2817264105331596"></elem>
   <br />
   With default mode, load time is 1.17s with 59 requests to transfer 5.3MB data from server to client. I think it is too heavy and too slow. We can check the header response of an image you can see as below:&nbsp;<br />
   <div class="format-code">
      <pre class=" language-other" tabindex="0"> <code class=" language-other">accept-ranges: bytes
content-length: 42182
content-type: image/jpeg
date: Fri, 25 Sep 2020 16:37:29 GMT
etag: "1d69283ade7ff46"
last-modified: Thu, 24 Sep 2020 15:02:11 GMT
server: Microsoft-IIS/10.0
status: 200
x-powered-by: ASP.NET</code></pre>
   </div>
   You can see it can not be cached here.<br />
   <h2 id="3_Test_performance_with_cache_HTTP_headers">3. Test performance with cache HTTP headers</h2>
   To <strong>enable Cache-Control in Asp.Net Core</strong> for all static files we need to add cache-control to the header response of each static file.<br />
   <strong>Step 1. Enable cookie policy.</strong><br />
   Open <code class="short-code">StartUp.cs</code> file, inside the method <code class="short-code">Configure()</code> we need to register the middleware cookie policy as below:<br />
   <div class="format-code">
      <pre class=" language-csharp" tabindex="0"> <code class=" language-csharp"><span class="token keyword keyword-public">public</span> <span class="token return-type class-name"><span class="token keyword keyword-void">void</span></span> <span class="token function">Configure</span><span class="token punctuation">(</span><span class="token class-name">IApplicationBuilder</span> app<span class="token punctuation">,</span> <span class="token class-name">IWebHostEnvironment</span> env<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
  <span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">-</span>
  app<span class="token punctuation">.</span><span class="token function">UseCookiePolicy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">-</span>
<span class="token punctuation">}</span></code></pre>
   </div>
   <strong>Step 2. Add Cache-Control to the HTTP header of the static file.</strong><br />
   To add cache-control to the response header of each response static file we need to custom middleware <code class="short-code">UseStaticFiles()</code> as below:<br />
   <div class="format-code">
      <pre class=" language-csharp" tabindex="0"> <code class=" language-csharp"><span class="token keyword keyword-public">public</span> <span class="token return-type class-name"><span class="token keyword keyword-void">void</span></span> <span class="token function">Configure</span><span class="token punctuation">(</span><span class="token class-name">IApplicationBuilder</span> app<span class="token punctuation">,</span> <span class="token class-name">IWebHostEnvironment</span> env<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span>
    app<span class="token punctuation">.</span><span class="token function">UseStaticFiles</span><span class="token punctuation">(</span><span class="token keyword keyword-new">new</span> <span class="token constructor-invocation class-name">StaticFileOptions</span>
    <span class="token punctuation">{</span>
        OnPrepareResponse <span class="token operator">=</span> context <span class="token operator">=&gt;</span>
        <span class="token punctuation">{</span>
            <span class="token comment">// add time expires header</span>
            <span class="token keyword keyword-const">const</span> <span class="token class-name"><span class="token keyword keyword-int">int</span></span> durationInSeconds <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">60</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">24</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">365</span><span class="token punctuation">;</span> <span class="token comment">// Expires time. I set to 1 year</span>
            context<span class="token punctuation">.</span>Context<span class="token punctuation">.</span>Response<span class="token punctuation">.</span>Headers<span class="token punctuation">[</span><span class="token string">"Cache-Control"</span><span class="token punctuation">]</span> <span class="token operator">=</span>
                <span class="token string">"public,max-age="</span> <span class="token operator">+</span> durationInSeconds<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span><span class="token operator">--</span>
<span class="token punctuation">}</span></code></pre>
      <span id="ezoic-pub-ad-placeholder-605" class="ezoic-adpicker-ad"></span>
      <span class="ezoic-ad box-4 box-4605 adtester-container adtester-container-605" data-ez-name="quizdeveloper_com-box-4" style="min-height: 280px;">
         <span id="div-gpt-ad-quizdeveloper_com-box-4-0" ezaw="336" ezah="280" style="position: relative; z-index: 0; padding: 0px; width: 100%; max-width: 1200px; min-height: 280px; min-width: 336px; margin-left: auto !important; margin-right: auto !important;" class="ezoic-ad ezfound" data-google-query-id="CKCj0s6y8fMCFQmUlgodSwAP6A">
            <div id="google_ads_iframe_/1254144/quizdeveloper_com-box-4_0__container__" style="border: 0pt none;"><iframe id="google_ads_iframe_/1254144/quizdeveloper_com-box-4_0" title="3rd party ad content" name="google_ads_iframe_/1254144/quizdeveloper_com-box-4_0" width="336" height="280" scrolling="no" marginwidth="0" marginheight="0" frameborder="0" sandbox="allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-top-navigation-by-user-activation" srcdoc="" style="border: 0px; vertical-align: bottom;" data-google-container-id="7" data-load-complete="true"></iframe></div>
         </span>
      </span>
   </div>
   <strong>In that :</strong><br />
   <code class="short-code"><strong>Max-age</strong></code> :<br />
   Time expires cache, after this total second that resource will be called to the server to reload again the resource file.<br />
   <code class="short-code">Cache-Control: Public</code>&nbsp;<br />
   The public response directive indicates that a resource can be cached by any cache.<br>Use this option when resources never or less update such as js, css, image.<br />
   <code class="short-code">Cache-Control: Private</code><br />
   This option indicates that all static files will be stored in the local memory of the end user.<br />
   <code class="short-code">Cache-Control: No-Cache</code><br />
   The response may be stored by any cache, even if the response is normally non-cacheable. However, the stored response MUST always go through validation with the origin server first before using it, therefore, you cannot use no-cache in-conjunction with immutable. If you mean to not store the response in any cache, use no-store instead. This directive is not effective in preventing caches from storing your response.<br />
   <code class="short-code">Cache-Control: No-Store</code><br />
   This option tells that browsers aren’t allowed to cache a response and must pull it from the server each time it’s requested. This setting is usually used for sensitive data, such as personal banking details.<br />
   And this is the result from the second request:<br />
   <p style="text-align:center">
      <elem><img alt="ASP.NET Core: Adding cache static file using HTTP headers" data-height="337" data-width="693" class=" ezlazyloaded" src="https://quizdeveloper.com/ezoimgfmt/img.quizdeveloper.com/Images/2020926712775ASP-NET-Core-Adding-cache-static-file-using-HTTP-headers_2.jpg?ezimgfmt=rs:693x337/rscb1/ng:webp/ngcb1" ezimgfmt="rs rscb1 src ng ngcb1" data-ezsrc="https://quizdeveloper.com/ezoimgfmt/img.quizdeveloper.com/Images/2020926712775ASP-NET-Core-Adding-cache-static-file-using-HTTP-headers_2.jpg?ezimgfmt=rs:693x337/rscb1/ng:webp/ngcb1" ezoid="0.1573300641953177"></elem>
   <br />
   Wow, still 59 requests, 5.5MB resources but now we only have 34.2kb transferred instead of 5.3MB and the time load is 442ms instead of 1.17s as above. It is too fast and load page faster than ~ 3 times.<br />
   And this is the response header of a static file with cache control:<br />
   <p style="text-align:center">
      <strong>
         <elem><img alt="ASP.NET Core: Adding cache static file using HTTP headers" data-height="379" data-width="632" class=" ezlazyloaded" src="https://quizdeveloper.com/ezoimgfmt/img.quizdeveloper.com/Images/20209268150ASP-NET-Core-Adding-cache-static-file-using-HTTP-headers_3.jpg?ezimgfmt=rs:632x379/rscb1/ng:webp/ngcb1" ezimgfmt="rs rscb1 src ng ngcb1" data-ezsrc="https://quizdeveloper.com/ezoimgfmt/img.quizdeveloper.com/Images/20209268150ASP-NET-Core-Adding-cache-static-file-using-HTTP-headers_3.jpg?ezimgfmt=rs:632x379/rscb1/ng:webp/ngcb1" ezoid="0.39713993975501394"></elem>
      </strong>
   <br />
   <strong>OTHER INFORMATION</strong><br>When you use cache-control I think you should also understand about <code class="short-code">Etag</code>, <code class="short-code">If-Match</code> and <code class="short-code">If-None-Match</code> header. Three of these options will help optimize better cache static files.<br />
   <code class="short-code">ETag</code> – A response header that identifies the version of served content according to a token – a string of characters in quotes, e.g., "0987656af37gfe98-ttgv4" – that changes after a resource is modified. If a token is unchanged before a request is made, the browser continues to use its local version.<br />
   You can read more them from <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag">Mozilla&nbsp;</a><br />
   <h2 id="4_Summary">4. Summary</h2>
   In this article, I guide how to enable <strong>Asp.Net Core cache control header</strong>? and see why it is important? This is a big point to increase Google page speed’s point. Cache-control headers help increase user experience better than when visiting a site.&nbsp;<br />
   You can download the source for reference <a href="https://github.com/quizdeveloper/CacheControlAsp.NetCore">here</a>.<br />
You can read original article from <a href="https://quizdeveloper.com/tips/aspdotnet-core-adding-cache-static-file-using-http-headers-aid69">here</a>, you also find other tech article and take some <a href="https://quizdeveloper.com">test programming skills</a> from it.
   Happy code!!!<br>&nbsp;<br />