Latest TweetsGreat post about the latest power grab: www.eff.org/deeplinks/2018/09/…
Perishable Press

HTTP Headers for ZIP File Downloads

You know when you you’re working on a project and get stuck on something, so you scour the Web for solutions only to find that everyone else seems to be experiencing the exact same thing. Then, after many hours trying everything possible, you finally stumble onto something that seems to work. This time, the project was setting up a secure downloads area for Digging into WordPress. And when I finally discovered a solution, I told myself that it was definitely something I had to share here at Perishable Press.

Apparently, there is much to be desired when it comes to sending proper HTTP headers for file downloads. Different browsers (and not just IE) require different headers, and will error if not present exactly the way they expected. Confounding that equation is the fact that different file types also require specific headers. Then there are issues with sending an accurate (or should I say “acceptable”?) Content-Length headers when file compression is involved. Needless to say, finding a set of headers that works for all file types in all browsers is next to impossible. And I won’t even get into the issues involved with readfile() and large-download file-sizes.

Download Headers that actually work

After trying hundreds of different headers and combinations, I hit upon a set that works great for ZIP downloads (and other file types as well) in all tested browsers. Here’s what they look like using PHP:

<?php // HTTP Headers for ZIP File Downloads
// https://perishablepress.com/press/2010/11/17/http-headers-file-downloads/

// set example variables
$filename = "Inferno.zip";
$filepath = "/var/www/domain/httpdocs/download/path/";

// http headers for zip downloads
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"".$filename."\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filepath.$filename));
ob_end_flush();
@readfile($filepath.$filename);
?>

This PHP script is known to work under the following conditions:

  • Operating System: Linux
  • Server: Apache/2.2.3 (CentOS)
  • MYSQL Version: 5.0.77-log
  • PHP Version: 5.2.6
  • PHP Safe Mode: Off
  • PHP Allow URL fopen: On
  • PHP Memory Limit: 256M
  • PHP Max Upload Size: 2M
  • PHP Max Post Size: 8M
  • PHP Max Script Execute Time: 30s

With this code, the downloads work in the following tested browsers:

  • Firefox 3.0, 3.5 (Mac & PC)
  • Opera 8, 9, 10 (Mac & PC)
  • Internet Explorer 7, 8
  • Chrome 7.0.517
  • Camino 2
  • Safari 5 (PC)
  • Safari 3 (Mac)

The downloads work for the following types of files (including small and large file types):

  • .zip
  • .txt
  • .pdf
  • .jpg

Obviously, I didn’t test every file type in every browser, but the positive results from those listed here suggest a much wider range of files and browsers that will work. For the file sizes, I tested small files only a few bytes in length, and also large files up to around 20MB or so. Also would like to give a shout to the Live HTTP Headers extension for Firefox. It proved indispensable throughout the troubleshooting/testing/pulling-my-hair-out process.

As always, if you can contribute to the content of this post with further information about sending proper HTTP Headers for file downloads, you may just save a life ;)

Jeff Starr
About the Author Jeff Starr = Creative thinker. Passionate about free and open Web.
Archives
25 responses
  1. if i may ask

    what should we code for download the zip of the directory
    the zip file is not exist, it is generated using PHP and containing all files of specified folder

    is it possible?

    Thanks

  2. I am not sure how this code is best integrated into WordPress … I can see how to use it in a normal php context, but WordPress has it’s complex get_header() getting in the way.

    Would it be possible to post an example of how to get this working with WordPress?

    Thank you.

  3. I had been getting corrupt zip files, and was searching everywhere for a fix, and these three lines just saved me.

    while (ob_get_level()) {
         ob_end_clean();
    }

    Thanks Vladimir!

  4. I saw smartReadFile (Reads the requested portion of a file and sends its contents to the client with the appropriate headers.)

    it started here
    http://php.net/manual/en/function.readfile.php#86244

    and was updated at the end of
    https://groups.google.com/forum/#!msg/jplayer/nSM2UmnSKKA/Hu76jDZS4xcJ

  5. Thanks for this. Scouring the web looking for a solution described me perfectly.

    I was getting odd characters – looked like a hex number – before any zip files that I was sending through a script. They’d download okay in Chrome, but when I tried to get it to download via fsockopen the file was invalid.

    But it’s working now, and I thank you for that.

  6. I am generating zipfile dynamically, in that case, how can I set filesize header?

  7. Sourabh Bajaj: You issue a SOAP request to http://god.org calling the PredictFileSizeOfMyZippedFilesPlease() method and use the response in the content-length header.

    …or you send the content-length header when the zipfile is completed and you actually *know* the filesize…

  8. That worked for me! ur awesome and wonderful for sharing this!!, may u have a wonderful day

  9. thank you

  10. hudson kotel September 6, 2012 @ 2:46 am

    hello,
    I found to zip file related to link in php. & it’s very useful link:-

    http://www.phphunt.com/131/zip-file-in-php

  11. i want to change the zip file name during download. please get me idea

  12. Thank you for that. It works for me fine.

[ Comments are closed for this post ]