Save 25% on our pro plugins with coupon code: SPRING2023
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 = Fullstack Developer. Book Author. Teacher. Human Being.
USP Pro: Unlimited front-end forms for user-submitted posts and more.

25 responses to “HTTP Headers for ZIP File Downloads”

  1. Avatar photo

    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. Avatar photo

    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. Avatar photo
    Sourabh Bajaj 2012/03/31 5:55 am

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

  7. Avatar photo

    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 »
The Tao of WordPress: Master the art of WordPress.
Excellent (and free) tool to test your site's SSL configuration.
Plugin updates! All of our free and pro plugins ready for WordPress 6.2.
Daylight savings is a complete waste of time and needs to be eliminated.
Got a couple of snow days here in mid-March. Fortunately it's not sticking.
I handle all email in real time as it comes in, perpetually clear inbox for years now.
Added some nice features to Wutsearch search engine launchpad. Now 21 engines!
.wp TLD plz :)
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.