Downloading files over SSL with Internet Explorer
Monday, June 22nd, 2009Everybody loves Internet Explorer, especially web developers. Rather than being a boring, predictable browser that does what you expect, there’s always a little quirk to keep you on your toes. There’s always some intriguing idiosyncrasy that ensures your day is not wasted by actually writing productive code, but instead spent trawling the statistical noise of Google in search of enlightenment.
Today I was informed by a customer that they couldn’t download a PDF document from a website I’d created for them. The PDF document was being accessed via the site’s administrative section, and was being downloaded via SSL. Of course, the predictably boring Firefox, Chrome, Safari and Operas of the world had no problems downloading the document, but Internet Explorer said this:
Internet Explorer cannot download [filename] from [website.com]
Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found. Please try again later.
The reason for this annoyance is that on the server, PHP automatically sets a “Cache-Control” HTTP header with a value of “Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0″ if you’re using sessions. This is usually a good thing, and ensures you always get an un-cached version of your dynamically generated pages, however in the case of downloading attachments, things go a little bit wrong. Here’s what happens:
- Client makes a request for a page over SSL
- Server says: You can’t cache this response
- Server starts sending a PDF document
- Client tries to store response in a temporary file until the whole PDF document has been downloaded
- Internet Explorer remembers that it’s been asked not to cache the file. Since we’re using SSL, it must be really important, so throws the error message above.
- All other browsers ignore the “cache-control” header and temporarily store the document anyway.
A few sites are claiming that this is a bug, but KB316431 says this behaviour is by design. The purist in me admits (rather grudgingly) that Microsoft might be right here, and they have implemented things correctly. This doesn’t please my client however, who still can’t download his PDF.
So we need a solution. The obvious answer is to get rid of the “no-cache” part of our headers. If you’re using PHP sessions however, it’s not that simple. Just adding:
< ?php
header("Cache-Control: private, max-age=1, pre-check=1");
?>
won’t work, as PHP’s session handling automatically adds it’s own “cache-control” header, and removes any others. The answer is as follows:
< ?php
// Get rid of anything in the output buffer
ob_clean();
// Determine the size of the download
$filesize = filesize($filename);
// Close and save any session variables
session_write_close();
// Write some headers, including a modified "cache-control"
header("Cache-Control: private, max-age=1, pre-check=1");
header("Pragma: none");
header("content-type: application/x-pdf");
header("content-disposition: attachment; filename=\"downloaded-file.pdf\"");
header("content-length: $filesize");
// Send the file contents to the output buffer
readfile($filename);
// And we're done
exit(0);
?>
The key here is to close the user session, preventing your “cache-control” header from being over-written by PHP automatically.
À bientôt!