title: Leverage browser caching in IIS (google pagespeed issue)
body: <p>There are several questions about leveraging browser caching but I didn't find anything useful for how to do this in an ASP.NET application. Google's Pagespeed tells this is performance biggest problem.
  So far I did this in my <em>web.config</em>:</p>
  
  <pre><code>&lt;system.webServer&gt;
    &lt;staticContent&gt;
      &lt;!--&lt;clientCache cacheControlMode="UseExpires"
              httpExpires="Fri, 24 Jan 2014 03:14:07 GMT" /&gt; --&gt;
      &lt;clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.24:00:00" /&gt;
    &lt;/staticContent&gt;
  &lt;/system.webServer&gt;
  </code></pre>
  
  <p>Commented code works. I can set expire header to be some particular time in future but I was not able to set <code>cacheControlMaxAge</code> to set how many days from now static content would be cached. It does not work. My questions is:</p>
  
  <p>How can I do that?
  I know it is possible to set caching only for specific folder which would be good solution, but it isn't working also. Application is hosted on Windows Server 2012,on IIS8, application pool is set to classic.</p>
  
  <p>After I set this code in web config I got pagespeed of 72 (before was 71). 50 files were not cached. (Now 49) I was wondering why and I just realized that one file was actually cached (svg file). Unfortunately png and jpg file were not.
  This is my <em>web.config</em></p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  
  &lt;configuration&gt;
    &lt;configSections&gt;
      &lt;section name="exceptionManagement" type="Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManagerSectionHandler,Microsoft.ApplicationBlocks.ExceptionManagement" /&gt;
      &lt;section name="jsonSerialization"     type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions,   Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E34" requirePermission="false" allowDefinition="Everywhere" /&gt;
      &lt;sectionGroup name="elmah"&gt;
        &lt;section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah"    /&gt;
        &lt;section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah"    /&gt;
        &lt;section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" /&gt;
        &lt;section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" /&gt;
      &lt;/sectionGroup&gt;
    &lt;/configSections&gt;
  
    &lt;exceptionManagement mode="off"&gt;
      &lt;publisher mode="off" assembly="Exception"  type="blabla.ExceptionHandler.ExceptionDBPublisher"  connString="server=188......;database=blabla;uid=blabla;pwd=blabla; " /&gt;
    &lt;/exceptionManagement&gt;
    &lt;location path="." inheritInChildApplications="false"&gt;
      &lt;system.web&gt;
        &lt;httpHandlers&gt;
          &lt;add verb="GET,HEAD" path="ScriptResource.axd"  type="System.Web.Handlers.ScriptResourceHandler,System.Web.Extensions, Version=1.0.61025.0,  Culture=neutral, PublicKeyToken=31bf3856ad364e34" validate="false" /&gt;
          &lt;add verb="GET" path="Image.ashx" type="blabla.WebComponents.ImageHandler, blabla/&gt;"
          &lt;add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" /&gt;
          &lt;add verb="*" path="*.jpg" type="System.Web.StaticFileHandler" /&gt;
          &lt;add verb="GET" path="*.js" type="System.Web.StaticFileHandler" /&gt;
          &lt;add verb="*" path="*.gif" type="System.Web.StaticFileHandler" /&gt;
          &lt;add verb="GET" path="*.css" type="System.Web.StaticFileHandler" /&gt;
        &lt;/httpHandlers&gt;
        &lt;compilation defaultLanguage="c#" targetFramework="4.5.1" /&gt;
        &lt;trace enabled="false" requestLimit="100" pageOutput="true" traceMode="SortByTime" localOnly="true"/&gt;
        &lt;authentication mode="Forms"&gt;
          &lt;forms loginUrl="~/user/login.aspx"&gt;
            &lt;credentials passwordFormat="Clear"&gt;
              &lt;user name="blabla" password="blabla" /&gt;
            &lt;/credentials&gt;
          &lt;/forms&gt;
        &lt;/authentication&gt;
        &lt;authorization&gt;
          &lt;allow users="*" /&gt;
        &lt;/authorization&gt;
        &lt;sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" timeout="20" /&gt;
        &lt;globalization requestEncoding="utf-8" responseEncoding="utf-8" culture="en-GB" uiCulture="en-GB" /&gt;
        &lt;xhtmlConformance mode="Transitional" /&gt;
        &lt;pages controlRenderingCompatibilityVersion="4.5" clientIDMode="AutoID"&gt;
          &lt;namespaces&gt;
  
          &lt;/namespaces&gt;
          &lt;controls&gt;
            &lt;add assembly="Microsoft.AspNet.Web.Optimization.WebForms" namespace="Microsoft.AspNet.Web.Optimization.WebForms" tagPrefix="webopt" /&gt;
          &lt;/controls&gt;
        &lt;/pages&gt;
        &lt;webServices&gt;
          &lt;protocols&gt;
            &lt;add name="HttpGet" /&gt;
            &lt;add name="HttpPost" /&gt;
          &lt;/protocols&gt;
        &lt;/webServices&gt;
      &lt;/system.web&gt;
    &lt;/location&gt;
    &lt;appSettings&gt;
  
    &lt;/appSettings&gt;
    &lt;connectionStrings&gt;
  
    &lt;/connectionStrings&gt;
    &lt;system.web.extensions&gt;
      &lt;scripting&gt;
        &lt;webServices&gt;
          &lt;jsonSerialization maxJsonLength="200000" /&gt;
        &lt;/webServices&gt;
      &lt;/scripting&gt;
    &lt;/system.web.extensions&gt;
    &lt;startup&gt;
      &lt;supportedRuntime version="v2.0.50727" /&gt;
      &lt;supportedRuntime version="v1.1.4122" /&gt;
      &lt;supportedRuntime version="v1.0.3705" /&gt;
    &lt;/startup&gt;
    &lt;system.webServer&gt;
  
  
      &lt;rewrite&gt;
        &lt;providers&gt;
          &lt;provider name="ReplacingProvider" type="ReplacingProvider, ReplacingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5ab632b1f332b247"&gt;
            &lt;settings&gt;
              &lt;add key="OldChar" value="_" /&gt;
              &lt;add key="NewChar" value="-" /&gt;
            &lt;/settings&gt;
          &lt;/provider&gt;
          &lt;provider name="FileMap" type="DbProvider, Microsoft.Web.Iis.Rewrite.Providers, Version=7.1.761.0, Culture=neutral, PublicKeyToken=0525b0627da60a5e"&gt;
            &lt;settings&gt;
              &lt;add key="ConnectionString" value="server=;database=blabla;uid=blabla;pwd=blabla;App=blabla"/&gt;
              &lt;add key="StoredProcedure" value="Search.GetRewriteUrl"/&gt;
              &lt;add key="CacheMinutesInterval" value="0"/&gt;
            &lt;/settings&gt;
          &lt;/provider&gt;
        &lt;/providers&gt;
        &lt;rewriteMaps configSource="rewritemaps.config" /&gt;
        &lt;rules configSource="rewriterules.config" /&gt;
      &lt;/rewrite&gt;
      &lt;modules&gt;
        &lt;remove name="ScriptModule" /&gt;
        &lt;add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3456AD264E35" /&gt;
        &lt;add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" /&gt;
        &lt;add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" /&gt;
        &lt;add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" /&gt;
      &lt;/modules&gt;
      &lt;handlers&gt;
        &lt;add name="Web-JPG" path="*.jpg" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" /&gt;
        &lt;add name="Web-CSS" path="*.css" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" /&gt;
        &lt;add name="Web-GIF" path="*.gif" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" /&gt;
        &lt;add name="Web-JS" path="*.js" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" /&gt;
      &lt;/handlers&gt;
      &lt;validation validateIntegratedModeConfiguration="false" /&gt;
      &lt;httpErrors errorMode="DetailedLocalOnly" existingResponse="Auto"&gt;
        &lt;remove statusCode="404" subStatusCode="-1"/&gt;
        &lt;remove statusCode="500" subStatusCode="-1"/&gt;
        &lt;error statusCode="404" path="error404.htm" responseMode="File"/&gt;
        &lt;error statusCode="500" path="error.htm" responseMode="File"/&gt;
      &lt;/httpErrors&gt;
    &lt;/system.webServer&gt;
    &lt;system.serviceModel&gt;
      &lt;bindings&gt;
        &lt;basicHttpBinding&gt;
          &lt;binding name="soapBinding_AdriagateService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true" messageEncoding="Text"&gt;
            &lt;readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" /&gt;
            &lt;security mode="None" /&gt;
          &lt;/binding&gt;
        &lt;/basicHttpBinding&gt;
        &lt;netTcpBinding&gt;
          &lt;binding name="NetTcpBinding_ITravellerService" closeTimeout="00:10:00" openTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647"&gt;
            &lt;readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" /&gt;
            &lt;security mode="None" /&gt;
          &lt;/binding&gt;
        &lt;/netTcpBinding&gt;
      &lt;/bindings&gt;
      &lt;client&gt;
        &lt;endpoint address="blabla" bindingConfiguration="soapBinding_blabla" contract="" Address="blabla" name="blabla" /&gt;
          &lt;endpoint address="blabla" binding="basicHttpBinding" bindingConfiguration="soapBinding_IImagesService"
            contract="ImagesService.IImagesService" name="soapBinding_IImagesService"/&gt;
          &lt;identity&gt;
            &lt;servicePrincipalName value="blabla"/&gt;
          &lt;/identity&gt;
        &lt;/endpoint&gt;
      &lt;/client&gt;
    &lt;/system.serviceModel&gt;
    &lt;runtime&gt;
      &lt;assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"&gt;
        &lt;dependentAssembly&gt;
          &lt;assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" /&gt;
          &lt;bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" /&gt;
        &lt;/dependentAssembly&gt;
        &lt;dependentAssembly&gt;
          &lt;assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /&gt;
          &lt;bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" /&gt;
        &lt;/dependentAssembly&gt;
      &lt;/assemblyBinding&gt;
    &lt;/runtime&gt;
    &lt;system.web&gt;
      &lt;httpModules&gt;
        &lt;add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" /&gt;
        &lt;add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" /&gt;
        &lt;add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" /&gt;
      &lt;/httpModules&gt;
    &lt;/system.web&gt;
    &lt;elmah&gt;
      &lt;security allowRemoteAccess="false" /&gt;
    &lt;/elmah&gt;
    &lt;location path="elmah.axd" inheritInChildApplications="false"&gt;
      &lt;system.web&gt;
        &lt;httpHandlers&gt;
          &lt;add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" /&gt;
        &lt;/httpHandlers&gt;
  
      &lt;/system.web&gt;
      &lt;system.webServer&gt;
        &lt;handlers&gt;
          &lt;add name="ELMAH" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" /&gt;
        &lt;/handlers&gt;
      &lt;/system.webServer&gt;
    &lt;/location&gt;
  &lt;/configuration&gt;
  </code></pre>
  
  <p><strong>EDIT:</strong>
  If I set exact expiry date, caching is working, but not for jpg,gif....only for png</p>
  
  <p><strong>EDIT2:</strong>
  If I set <code>cacheControlCustom="public"</code> like here:</p>
  
  <pre><code>&lt;clientCache cacheControlCustom="public" 
  cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" /&gt; 
  </code></pre>
  
  <p>caching is working but <strong>still</strong> not for jpegs and gifs; it only works for svgs and pngs.</p>