Downloading Microsoft office files under SSL with CacheControl=no-cache

 

My current project involves an ASP.NET application which need to be able to send some protected data. SSL was used to encrypt the sensitive pages, and the rest of the site has encryption turned off. The reporting side of the application, which allows the user to download dynamically generated reports was resulting in the following error:

image

This seemed to be happening in the following circumstances:

  1. Internet Explorer had the Internet Options > Advanced > Do not save encrypted pages to disk.
  2. The application was modifying a response (POST).
  3. It was sending an office document.

Now this particular page had encryption turned off, but it was still being accessed via HTTPS. Taking a look a look through fiddler was showing that CacheControl was being set to no-cache:

POST /Something.com/Reports/UserReports.aspx HTTP/1.1
Accept: application/x-ms-application, application/vnd.ms-excel
Referer: https://something.com/Something/Reports/UserReports.aspx
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: usermanager.something.com
Content-Length: 265
Connection: Keep-Alive
Cache-Control: no-cache

After some investigation it turns out this was a known issue from Microsoft which has persisted through Internet Explorer versions; Opera and Firefox apparently do not share this issue.

There are a number of possible solutions, and out of them we could not:

  1. Change the group policy around caching files under SSL.
  2. Run the reports under HTTP.
  3. Follow the support article and hack the registry (why is this even noted as a resolution?) as this overrides the group policy.

The solution in this circumstance was to remove the Cache-Control:no-cache portion of the header and set it to something Internet Explorer will be able to consume. The following code was causing this issue:

private static void SendExcelReport(string filename, MemoryStream stream)
{
    var response = HttpContext.Current.Response;

    response.ContentType = "application/vnd.ms-excel";
    
    response.AddHeader(
        "Content-Disposition", 
        string.Format("attachment;filename={0}", filename));

    response.Clear();
    stream.Seek(0, 0);
    byte[] bytes = stream.GetBuffer();
    response.BinaryWrite(bytes);
    response.Flush();
    response.End();
}

When using IIS and integrated pipeline mode, you can manipulate the header programatically:

response.Headers["Cache-Control"] = "private";

However if you try this with IIS6 or when running in Classic Pipeline mode, you will get the following error:

This operation requires IIS integrated pipeline mode.

Oh dear, however the solution wasn’t too bad:

private static void SendExcelReport(string filename, MemoryStream stream)
{
    var response = HttpContext.Current.Response;

    response.ClearHeaders(); // Clear the header, including no-cache
    response.ContentType = "application/vnd.ms-excel";
    response.AddHeader(
        "Content-Disposition", 
        string.Format("attachment;filename={0}", filename));

    response.Clear();
    stream.Seek(0, 0);
    byte[] bytes = stream.GetBuffer();
    response.BinaryWrite(bytes);
    response.Flush();
    response.End();
}

Internet Explorer was now happily allowing the Open / Save / Cancel dialog without the error message.

HTTP/1.1 200 OK
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: application/vnd.ms-excel
Server: Microsoft-IIS/7.5
X-AspNet-Version: 2.0.50727
Content-Disposition: attachment;filename=Report.xls
X-Powered-By: ASP.NET
Date: Wed, 14 Apr 2010 14:53:25 GMT

 

IIS has defaulted the Cache-Control to private, but this could be set to whatever you decide based on the IIS configuration or the AddHeader method.

April 15 2010
Comments are closed