New Bookstore! Save 20% on books with discount code: LAUNCH
Web Dev + WordPress + Security

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

// set example variables
$filename = "";
$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));

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.
WP Themes In Depth: Build and sell awesome WordPress themes.

25 responses to “HTTP Headers for ZIP File Downloads”

  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?


  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()) {

    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

    and was updated at the end of!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. Sourabh Bajaj 2012/03/31 5:55 am

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

  7. Sourabh Bajaj: You issue a SOAP request to 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 2012/09/06 2:46 am

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

  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. Something to add? Let me know.
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
WP Themes In Depth: Build and sell awesome WordPress themes.
Take a screenshot with Firefox (no extension required). Open Developer Tools Settings and enable the “Take a screenshot” button. Then click the button :)
Take a screenshot with Chrome (no extension required). Open DevTools, type Cmd + Shift + P, then type screenshot.
After 10 years working on my 2010 iMac, my upgrade finally arrived. Shiny new iMac shipped from Ireland :)
Too much caffeine weirds me out. But I love the taste of coffee. So once in a while I enjoy a small cup of decaf. Hits the spot.
Chris Coyier is a truly awesome person. One of the finest people I've ever worked with. Just #gottasayit
Excel won't open CSV file because SYLK format? Open it with text editor and add an apostrophe ' at the beginning of the file, save changes, done.
Displaying too many social media buttons and links all over the place imho makes you look desperate and frankly kinda sad.
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.