Sometimes files to be downloaded need to be protected, for example when download is available only for registered users of a website. To do this we need to make a “wrapper” script which checks if the file to be downloaded is available or not and we have to put the file in a place on our web server where they cannot be accessed through a url. But using Internet Explorer we can have some trouble to let our users download the files correctly. Now I’ll make an example using PHP as server-side scripting language.

Consider that you have a directory structure similar to this:

- /var
- – /www
- – - /mysite
- – - – /public_html
- – - – /downloads

In this structure only pages (and files) in the “public_html” directory can be accessed with a URL and the “downloads” folder contains the “protected” files to be downloaded. So in the “public_html” folder we can put a script to provide dowloads, and call it “download.php” (remember PHP can access the whole server’s filesystem). We also assume we have a database table where files are stored and identified by IDs and that we can pass these IDs as GET parameters to the script.

ob_start();
include ("useful_functions.php"); // We need some extra functions, which won't be defined here...
session_start();
$file_id = $_GET['id'];
$user_id = $_SESSION['userid'];
if (userCanDownload($user_id,$file_id)){ # assume this function returns a boolean value
    $file_path = getFilePath($file_id);     # assume these following functions return a string
    $file_name = getFileName($file_id);
    $file_content_type = getFileContentType($file_id);
    ob_end_clean(); # We need to clean all possible output data before reading the file
    // Set the correct headers
    header ('Content-type: '.$file_content_type);
    header ('Content-disposition: attachment; filename="'.$file_name.'"');
    readfile($file_path);
} else {
  ob_end_flush(); # If there was output in previous line, display it
  echo "Download not permitted";
}

This script won’t work using IE as you can see at the header function page of the PHP online manual.
For it we just add some extra headers before reding the file:

header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

Using Internet Explorer can bring some other errors especially if mod_gzip is activated on the web server. Mod_gzip is an extension to the apache web server which compresses using GZIP all Apache’s output before sending to the browser in order to use less bandwith e let downloads to be faster.
While Firefox doesn’t make any control, Internet Explorer does. IE doesn’t look the content-type header, it tries to identify the file type while reading the first bytes of the file to be downloaded. But using mod_gzip, all files are Gzipped archives! If the Content-type specified in the header section doesn’t match the one IE identifies, content-type will be ignored, so all the files will be identified as ‘compressed folders’ and content-disposition filename attribute will be ignored as well. To avoid this we can disable mod_gzip placing this script piece before the headers’ section:

apache_setenv("no-gzip","1");

Other tricks that will make downloads easier using Internet Explorer is to put in your “download.php” script:

ini_set("zlib.output_compression","Off"); # Disable PHP's output compression

header ("200 HTTP/1.0 OK"); # Force the answer to be HTTP/1.0 compatible

Summarizing all, our download.php script should be like this:

ob_start();
include ("useful_functions.php"); // We need some extra functions, which won't be defined here...
session_start();
$file_id = $_GET['id'];
$user_id = $_SESSION['userid'];
if (userCanDownload($user_id,$file_id)){ # assume this function returns a boolean value
    $file_path = getFilePath($file_id);     # assume these following functions return a string
    $file_name = getFileName($file_id);
    $file_content_type = getFileContentType($file_id);
    apache_setenv("no-gzip","1");
 
 
    ini_set("zlib.output_compression","Off"); # Disable PHP's output compression
    ob_end_clean(); # We need to clean all possible output data before reading the file
    // Set the correct headers
    header ("200 HTTP/1.0 OK"); # Force the answer to be HTTP/1.0 compatible
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header ('Content-type: '.$file_content_type);
    header ('Content-disposition: attachment; filename="'.$file_name.'"');
    readfile($file_path);
} else {
  ob_end_flush(); # If there was output in previous line, display it
  echo "Download not permitted";
}