|
|
 |
header (PHP 3, PHP 4, PHP 5) header -- Send a raw HTTP header Descriptionvoid header ( string string [, bool replace [, int http_response_code]] )
header() is used to send raw
HTTP headers. See the HTTP/1.1 specification for more
information on HTTP headers.
The optional replace parameter indicates
whether the header should replace a previous similar header, or
add a second header of the same type. By default it will replace,
but if you pass in FALSE as the second argument you can force
multiple headers of the same type. For example:
The second optional http_response_code force the
HTTP response code to the specified value. (This parameter is available
in PHP 4.3.0 and higher.)
There are two special-case header calls. The first is a header
that starts with the string "HTTP/" (case is not
significant), which will be used to figure out the HTTP status
code to send. For example, if you have configured Apache to
use a PHP script to handle requests for missing files (using
the ErrorDocument directive), you may want to
make sure that your script generates the proper status code.
Note:
The HTTP status header line will always be the first sent
to the client, regardless of the actual header()
call being the first or not. The status may be overridden
by calling header() with a new status line
at any time unless the HTTP headers have already been sent.
The second special case is the "Location:" header. Not only does
it send this header back to the browser, but it also returns a
REDIRECT (302) status code to the browser unless
some 3xx status code has already been set.
Note:
HTTP/1.1 requires an absolute URI as argument to
Location:
including the scheme, hostname and absolute path, but
some clients accept relative URIs. You can usually use
$_SERVER['HTTP_HOST'], $_SERVER['PHP_SELF']
and dirname() to make an absolute URI from a
relative one yourself:
PHP scripts often generate dynamic content that must not be cached
by the client browser or any proxy caches between the server and the
client browser. Many proxies and clients can be forced to disable
caching with:
Note:
You may find that your pages aren't cached even if you don't
output all of the headers above. There are a number of options
that users may be able to set for their browser that change its
default caching behavior. By sending the headers above, you should
override any settings that may otherwise cause the output of your
script to be cached.
Additionally, session_cache_limiter() and
the session.cache_limiter configuration
setting can be used to automatically generate the correct
caching-related headers when sessions are being used.
Remember that header() must be
called before any actual output is sent, either by normal HTML
tags, blank lines in a file, or from PHP. It is a very common
error to read code with include(), or
require(), functions, or another file access
function, and have spaces or empty lines that are output before
header() is called. The same problem exists
when using a single PHP/HTML file.
Note:
As of PHP 4, you can use output buffering to get around this problem,
with the overhead of all of your output to the browser being buffered
in the server until you send it. You can do this by calling
ob_start() and ob_end_flush()
in your script, or setting the output_buffering
configuration directive on in your php.ini or
server configuration files.
If you want the user to be prompted to save the data you are
sending, such as a generated PDF file, you can use the Content-Disposition header to
supply a recommended filename and force the browser to display the
save dialog.
Note:
There is a bug in Microsoft Internet Explorer 4.01 that prevents
this from working. There is no workaround. There is also a bug
in Microsoft Internet Explorer 5.5 that interferes with this,
which can be resolved by upgrading to Service Pack 2 or later.
Note:
If safe mode is enabled the
uid of the script is added to the realm part
of the WWW-Authenticate header if you set
this header (used for HTTP Authentication).
See also headers_sent(),
setcookie(), and the section on
HTTP authentication.
User Contributed Notes
header
com dot gmail at schaefer dot peter
10-May-2005 01:02
Regarding the IE pdf problem, doing the variation of not sending an
Accept-Ranges: bytes
header seems to make a difference. Strangely enough, some pdfs just work fine without these complications, so maybe changing that header just forced invalidation of the IE cache(I had emptied it, but who knows how IE really works).
//header("Accept-Ranges: bytes");
$filename= 'Labels'. date("ymdhis") . '.pdf';
//FIX: IE6
header('Content-type: application/pdf');
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
//header("Cache-Control: private");
$pdf->stream( array('Content-Disposition'=>$filename) );
This is a large pdf file.
For a smaller pdf file, the straightforward thing worked too:
header("Accept-Ranges: bytes");
header("Cache-Control: private");
$pdf->stream();
Weird :-/
darkstar_ae at hotmail dot com
08-May-2005 09:02
To spare some of you the mental anguish ang torture, be wary of line feeds & blank spaces in your script before calling the header() function.
example:
start of file ----
<?
header('file.php');
?>
<html>
.
.
.
html codes follow
This may look like an innocent and correct script but the space between the imaginary start of file marker and the "<?" will be read as a sent header to the browser. You may end up spending hours trying to to debug the script and might even end up looking through the server configs figuring this out. This problem is not so obvious.
This solution is only applicable to php configurations that don't use output buffering.
Happy hunting. ;)
wil1488 at gmail dot com
08-May-2005 05:22
vernonj
said abolt the problem:
'Warning: Cannot modify header information - headers already sent by'
and the solution is use:
<? echo "<script> self.location(\"file.php\");</script>"; ?>
i think the better method is use:
<?
print("<script type="text/javascript">window.location=\"file.php\"</script>");
?>
// i use print, but echo works great! :)
i tested this method to redirect on Opera and Internet Explorer, maybe works on all browsers!
This method of redirect with javascript i found on: http://www.w3schools.com/js/tryit.asp?filename=tryjs_crossbrowser
Thanks !
TJ
04-May-2005 05:49
I had made a download script for a client that would just register the number of times a document had been downloaded in a database. When using headers and the readfile()-function to pass the file through the browser I got some weird errors with half of the powerpoint files I tried to download.
My solution:
- Register the hitcount in the database
- Instead of using headers and readfile, forward the user to the file he/she originally requested by using the following code:
header("HTTP/1.0 307 Temporary redirect");
echo header("Location: http://www.example.com/filename"); exit();
mephistos at ighettomail dot com
28-Apr-2005 01:06
I hope this little redirection function might be useful to someone:
<?
function redirect($url = NULL, $relative = FALSE) {
header("Location: http://".$_SERVER['HTTP_HOST'].(($relative) ? dirname($_SERVER['PHP_SELF'])."/" : "/").$url);
exit;
}
?>
If no url is specified it will redirect to server root and if relative is set to true it will redirect using a relavite path.
pornel at despammed dot com
28-Apr-2005 10:00
Please read caching tutorial and test scripts with: http://www.web-caching.com/cacheability.html before copying & pasting all "urban legend" caching headers.
Another note:
Content-Disposition is non-standard header and sending files with readfile() doesn't support resuming, caching and might be problematic with large files. In some cases it's better to use Apache mod_rewrite instead.
Oto "Zver" Brglez ml.
24-Apr-2005 07:40
I have a page that should also be used for mobile devices. So i have hade some throubles. I wrote this script for detection and redirection using header.
function Embended(){ /* Function for detection */
$out = false;
$STREZNIK = strtoupper($_SERVER['HTTP_USER_AGENT']);
if(eregi('NOKIA',$STREZNIK)) $out = true;
if(eregi('MOT',$STREZNIK)) $out = true;
if(eregi('SEC-',$STREZNIK)) $out = true;
if(eregi('PHONE',$STREZNIK)) $out = true;
if(eregi('PANASONIC',$STREZNIK)) $out = true;
if(eregi('SEC-',$STREZNIK)) $out = true;
if(eregi('PPC',$STREZNIK)) $out = true;
if(eregi('SIEMENS',$STREZNIK)) $out = true;
if(eregi('MITSU',$STREZNIK)) $out = true;
if(eregi('PORTALMMM',$STREZNIK)) $out = true;
if(eregi('BLACKBERRY',$STREZNIK)) $out = true;
if(eregi('SYMBIAN',$STREZNIK)) $out = true;
if(eregi('PHILIPS',$STREZNIK)) $out = true;
if(eregi('SENDO',$STREZNIK)) $out = true;
if(eregi('KLONDIKE',$STREZNIK)) $out = true;
if(eregi('SAGEM',$STREZNIK)) $out = true;
if(eregi('MOBILE',$STREZNIK)) $out = true;
if(eregi('ALCATEL',$STREZNIK)) $out = true;
if(eregi('SONY',$STREZNIK)) $out = true;
return $out;
};
/* Redirection with header to mobile page */
if(Embended()){ header("Location: zooxMobile.php"); };
mjs15451 at hotmail dot com
22-Apr-2005 02:04
I was looking at this section and I noticed that it didn't mention anything about javascript includes using php files. I tested this code on Gecko browsers (Firefox, Mozilla, Netscape), MSIE and Opera and they all can include the php file with this header:
header("Content-type: application/x-javascript");
This would be the code you would use to include it:
<script type="text/javascript" src="javascript.php"></script>
MSIE doesn't understand the file only if it is called directly in the location bar; it still executes the file when called through html.
I believe this header is necessary for legacy browsers, i.e. Netscape 4.x or less.
Philipp Heckel
19-Apr-2005 02:16
Sending image files through PHP is very easy, if you don't want to use the browser cache. As soon as you like to use the cache, it is getting a little more complicated. As it isn't possible to define all headers by yourself, and PHP likes sending 'Pragma: no-cache', 'Cache-Control: ...' and other funny headers preventing the browser of caching the file, we need to modify these lines.
<?php
if (!@file_exists($filename) || !@is_readable($filename)) die("The selected Image does not exist.");
else $lastmod = @filemtime($filename) or die("Error while trying to read the 'Last Modified'-Information.");
$sendbody = true;
$content = file_get_contents($filename);
$etag = '"'.md5($content).'"';
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && gmdate('D, d M Y H:i:s', $lastmod)." GMT" == trim($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
header("HTTP/1.0 304 Not Modified");
header("ETag: {$etag}");
header("Content-Length: 0");
$sendbody = false;
}
if ($sendbody && isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$inm = explode(",",$_SERVER['HTTP_IF_NONE_MATCH']);
foreach ($inm as $i) {
if (trim($i) != $etag) continue;
header("HTTP/1.0 304 Not Modified");
header("ETag: {$etag}");
header("Content-Length: 0");
$sendbody = false; break;
}
}
$expires = 60 * 60 * 24;
$exp_gmt = gmdate("D, d M Y H:i:s",time()+$expires)." GMT";
$mod_gmt = gmdate("D, d M Y H:i:s",$lastmod)." GMT";
header("Expires: {$exp_gmt}");
header("Last-Modified: {$mod_gmt}");
header("Cache-Control: public, max-age={$expires}");
header("Pragma: !invalid");
if ($sendbody) {
$size = @filesize($filename) or openError("Error while trying to read the Filesize",lastURI());
header("ETag: {$etag}");
header("Content-Type: image/jpeg");
header("Content-Length: {$size}");
echo $content;
}
else {
header("Content-Type: !invalid");
}
exit;
?>
Note that both Firefox and the IE reload the image if the 'Content-Type: !invalid'- and the 'Pragma: !invalid'-lines are left out. I tried 'Content-Type: ' and 'Pragma: ', but this did not work.
Instead of these !invalid-values, you can use session_cache_limiter("public") before starting the session with session_start(). This will also avoid sending the Pragma-header.
Philipp Heckel
mike at go dot online dot com
11-Apr-2005 03:14
Here's a way of forcing the use a Canonical Hostname, e.g www.domain.com instead of domain.com
<?php
if ($HTTP_HOST != $SERVER_NAME) header("Location: http://www.domain.com");
?>
Bill Kuker
07-Apr-2005 11:30
I am serving an XSL file generated by php for client side xslt processing. Where all of my static *.xsl files worked fine my dynamicXSL.php file would not work with IE, which would say:
The XML page cannot be displayed
Cannot view XML input using XSL style sheet. Please correct the error and then click the Refresh button, or try again later.
The download of the specified resource has failed.
(I include the whole message in hopes that the next guy googling for it has an easier time).
header("Content-type: text/xml");
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
made it work.
nbilalis at yahoo dot com
06-Apr-2005 06:20
A more general approach for the redirection could be:
<?php
header("Location: http" . ($_SERVER["HTTPS"]=='on'?"s":"") . "://".$_SERVER['HTTP_HOST']
.dirname($_SERVER['PHP_SELF'])
."/".$relative_url);
?>
This way you wont mind if later you wanna use SSL.
james at charity dot org
05-Apr-2005 11:15
If you're creating a download page for Internet Explorer, the following code works:
if(isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/MSIE/", $_SERVER['HTTP_USER_AGENT'])) {
// IE Bug in download name workaround
ini_set( 'zlib.output_compression','Off' );
}
header('Content-type: ' . $file['type']);
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Disposition: attachment; filename="' . $file['filename'] . '"');
print($file['data']);
I will note that setting content length or size headers will cause IE to see the file being opened/saved as 0 bytes and will on occasion open the content of the file in the browser itself after the download fails.
I also recieved file not found problems until I added the Pragma and Cache-Control headers.
Yet another one of those "Thanks Bill" situations.
--james
cbwhiz at hotmail dot com
03-Apr-2005 05:25
"benjamin at sonntag dot fr
11-Mar-2005 07:03
About michael and it's header "Refresh", be careful, The HTTP standard requires the following syntax :
<?php
header("Refresh: 0; URL=http://www.php.net");
?>
this is in fact the only way to prevent the multi-post issue with all browsers (ie, firefox ...)"
I disagree. This is the correct way:
<?PHP
function postredir($url) {
header('HTTP/1.1 303 See Other');
header("Location: ".$url);
echo "We are now redirecting you to <a href='".urlencode($url)."'>$url</a>...";
}
?>
303 See Other means convert any request into a GET request - i.e. even if it submitted a POST request to this url, use GET on the redirected URL.
snagnever at gmail dot com
31-Mar-2005 03:56
Ok, maybe it can be usefull -- i was trying to save space on my disk and my transfer-rate, but i didn't want to sacrifice the quality of my pictures.
So, it can be usefull for people like me.
The file, when is sent to the server, is compressed in gzip:
<?php
$content = file_get_contents($_FILES['userfile']['tmp_name']);
$content = gzencode($content,9);
$id = fopen("/new/path/images/thename.png.gz","w+");
fwrite($id,$content);
fclose($id);
?>
So, it is saved in a new path, that cannot be accessed remotely, to avoid unwanted remote-users accessing it.
But there's a script that offers it to the user: show_image.php?image=thename.png
<?php
$file_path = "/new/path/images/";
$file_name = basename($_GET['image']);
if( file_exists($file_path . $file_name . ".gz") ){
header("Content-Encoding: gzip");
header("Content-type: image/png");
$content = file_get_contents( $file_path . $file_name . ".gz" );
echo $content;
} else {
echo "The file <i> $file_names </i> does not exist.";
}
?>
Maybe you want to control if the client does support the gzip compression, so u can use it:
<?php
if( eregi("gzip",$_SERVER['HTTP_ACCEPT_ENCODING']) ){
} else {
echo "Your browser does not support gzip compression. Update it.";
}
?>
Juan
26-Mar-2005 06:13
I had a lot of problems with using header() to redirect to a page because somewhere in my code I was sending content out too soon. To overcome this, I used ob_start() to buffer the output, allowing headers to go out before content. A nice little work-around, but not perfect.
oto_brglez_ml at hotmail dot com
20-Mar-2005 04:46
I'm using this lines to force users of my page to use SSL. U can set variable $SSL to true if u want SSL and false if u don't. This snippet is from my login page where i want to use SSL.
$SSL = true;
if($SSL){ /* I want to use SSL on my page */
if($_SERVER["HTTPS"] == 'on'){ /* SSL is used */
/* User exists so let's send header and rederect him to page zoox.php */
if($_SESSION['id']!=''){ header("Location: zoox.php"); };
} else {
/* SSL is not used so rederect him to this page this time with SSL */
header("Location: https://".$_SERVER["SERVER_NAME"].$_SERVER["PHP_SELF"]);
};
} else { /* I don't want to use SSL on my page */
/* User exists so let's go to zoox.php without SSL */
if($_SESSION['id']!='') header("Location: zoox.php");
};
Aakash Shah
19-Mar-2005 11:52
When using header(), beware that an HTML comment and a blank line can also prevent header() from working and getting the message "headers cannot be modified...". Here is what I mean:
___START OF FILE1.php___
<?php
header('Location: file2.php');
?>
___END OF FILE1.php___
___START OF FILE2.php___
<!-- An HTML comment -->
<?php
header('Location: file2.php');
?>
___END OF FILE2.php___
So, if anyone is having problems with this, remember to remove any HTML comments.
Mike
15-Mar-2005 08:47
Just to reiterate Jurgen Westerhof's observation
earlier. If you are struggling with IE making a mess of
Content-Disposition headers, then switch off
zlib.output_compression.
If it is enabled, IE tries to use the script name rather
than the supplied filename in the Content-Disposition
header, with very bizarre (and totally useless) results.
if(isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/MSIE/", $_SERVER['HTTP_USER_AGENT'])) {
// IE Bug in download name workaround
ini_set( 'zlib.output_compression','Off' );
}
header( 'Content-Type: application/octet-stream' );
header( 'Content-Size: $fileSize );
header( "Content-Disposition: attachment; filename=\"$fileName\"");
@readfile($filepath);
Finally worked for me.
markus dot amsler at oribi dot org
14-Mar-2005 11:19
I had some troubles with additional numbers being sent in the raw http response. This was because of the chunked encoding of the http response. To disable chunking you can simply send a Content-Length header with the apropriate length set.
benjamin at sonntag dot fr
11-Mar-2005 08:03
About michael and it's header "Refresh", be careful, The HTTP standard requires the following syntax :
<?php
header("Refresh: 0; URL=http://www.php.net");
?>
this is in fact the only way to prevent the multi-post issue with all browsers (ie, firefox ...)
fedenuche at gamail dot com
10-Mar-2005 10:03
These headers only works with include, css, js, html, etc. But doesn't work with images files (jpg, gif, etc.)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
To no cache images you can use a trick:
$var = md5(time());
$picture_name = "picture.jpg?var=".$var;
picture.jpg is the name of the file that you don't want cache.
sirdan at alanfa dot de
08-Mar-2005 06:00
a correction of michael at whypsloven dot net post:
// Redirects the browser after $sec seconds
header("Refresh: $sec; http://www.php.net");
should be:
header("Refresh: $sec; url=http://www.php.net");
ninj at chello dot fr
03-Mar-2005 09:18
A note about the redirection:
the way to redirect is:
header ("location: target.php");
and it is advised to add, before, a:
header ("HTTP/1.0 307 Temporary redirect");
if the redirection is temporary.
But if the redirection occurs after a the submission of a post form, your browser will ask you if you want to keep the post data for the redirected page! This could be very annoying in the case the redirection is wanted.
The solution to avoid this browser question is not to add any redirection info header at all.
So nor the temporary neither the permanent redirect are to use in the case of a redirection after a post (login page for example).
michael at whypsloven dot net
01-Mar-2005 12:41
To redirect the user a certain amount of time after displaying content, use the Refresh, not the Location header:
<?php
header("Location: http://www.php.net");
echo "This content is not displayed.";
header("Refresh: 0; http://www.php.net");
echo "This content is usually displayed by the browser in the instant before the next page can be loaded.";
$sec = 10;
header("Refresh: $sec; http://www.php.net");
echo "This content is displayed for $sec seconds, then the browser will redirect.";
?>
Hayley Watson
01-Mar-2005 03:03
php at richardneill dot org's confusion about when to use & and when to use & can be resolved by remembering the difference between HTML and http. Headers (where you use &) are part of http responses (which might or might not include HTML content), while links inside HTML pages (where you use &) are part of HTML (which might or might not be contained in an http response). Keep the difference in mind and it won't be mistaken for perversity!
jukkissh at hotmail dot com
25-Feb-2005 05:04
If you are building a download script and you are afraid of someone exploiting it, I got a solution.
Store the information of the downloadable files into a (SQL or text file) database or even in array variable in the code if your list of files is very static. You should store at least the path & filename and unique id-number. You can be creative when thinking what info to store...
Build a download script that GETs an id number and checks the database for the file with the given id. Then force download for that file, if it's found, otherwise print an error message.
Example use:
http://some.host.com/download.php?id=256
--> Downloading file...
http://some.host.com/download.php?id=h4x.txt
--> Error! File not found!
The force-download script can be built many ways stated in this page (in the notes at least). Pick one that forces the download well. It does not need any extra security features, because we got them already.
php at richardneill dot org
24-Feb-2005 08:53
nickesh at o2 dot pl
21-Feb-2005 02:36
I've tried to use the script written above...
(aarondunlap.com 28-Dec-2004 11:17)
It doesn't work for PDF extenction corectly. The file is downloaded but as PHP file, not PFD.
I've removed: $ctype="application/pdf"; from the script (in: case "pdf").
Now it works, but don't ask me why.
vernonj at gmail dot com
17-Feb-2005 09:36
when redirecting to another page
<? header(Location: file.php); ?>
you must make sure you put the header function before all output..otherwise you'll end up with an error such as:
'Warning: Cannot modify header information - headers already sent by'
however this causes great inconvenience, a quick work around is to use javascript to allow you to redirect to any page from anywhere in your script no matter if output has already started, by using:
<? echo "<script> self.location(\"file.php\");</script>"; ?>
crispiness at fastmail dot fm
11-Jan-2005 10:09
If the below post didn't make this clear... be VERY VERY CAREFUL with download scripts! I had a vulnerability for years in my download counting script; namely, the user could download ANY file on the server. Including the PHP files that contained my database password!
The same potential vulnerability applies to any script that displays or downloads files from your server. Caveat scriptor (let the programmer beware; and no, that's not proper Latin)!
aarondunlap.com
28-Dec-2004 04:17
I just made a function to allow a file to force-download (for a script to disallow file links from untrusted sites -- preventing mp3/video leeching on forums), and I realized that a script like that could potentially be very dangerous.
Someone could possibly exploit the script to download sensitive files from your server, like your index.php or passwords.txt -- so I made this switch statement to both allow for many file types for a download script, and to prevent certain types from being accessed.
<?php
function dl_file($file){
if (!is_file($file)) { die("<b>404 File not found!</b>"); }
$len = filesize($file);
$filename = basename($file);
$file_extension = strtolower(substr(strrchr($filename,"."),1));
switch( $file_extension ) {
case "pdf": $ctype="application/pdf"; break;
case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "doc": $ctype="application/msword"; break;
case "xls": $ctype="application/vnd.ms-excel"; break;
case "ppt": $ctype="application/vnd.ms-powerpoint"; break;
case "gif": $ctype="image/gif"; break;
case "png": $ctype="image/png"; break;
case "jpeg":
case "jpg": $ctype="image/jpg"; break;
case "mp3": $ctype="audio/mpeg"; break;
case "wav": $ctype="audio/x-wav"; break;
case "mpeg":
case "mpg":
case "mpe": $ctype="video/mpeg"; break;
case "mov": $ctype="video/quicktime"; break;
case "avi": $ctype="video/x-msvideo"; break;
case "php":
case "htm":
case "html":
case "txt": die("<b>Cannot be used for ". $file_extension ." files!</b>"); break;
default: $ctype="application/force-download";
}
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: $ctype");
$header="Content-Disposition: attachment; filename=".$filename.";";
header($header );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$len);
@readfile($file);
exit;
}
?>
This works in both IE and Firefox.
imoore76 at yahoo dot com
14-Dec-2004 03:12
I'd been trying to figure out why I couldn't get Internet Explorer to download a file if I used session_start(). It only happened with certain files. Here's the undocumented (I couldn't find anything on it) skinny that I've come up with:
If the content type (sent in the Content-type header) is not known to Windows AND the cache-control header contains 'no-store' or 'no-cache' I.E. will error out. I'm using WindowsXP Home and I.E. 6.0.
Both 'no-store' and 'no-cache' cause the error by themselves and individually. I've found that always replacing the header with one that excludes those lines works:
<?
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
?>
This error would happen when using any PHP function (or anything else for that matter) that may set 'no-store' and/or 'no-cache' in the Cache-control header. Provided the content type is not known to Windows.
E.g.
<?
header("Content-type: text/plain");
header("Cache-Control: no-store, no-cache");
echo($data);
header("Content-type: foo/bar");
header("Cache-Control: no-store, no-cache");
echo($data);
?>
On a side note, content types are kept in the registry under HKEY_CLASSES_ROOT under each file extension. My downloads were failing with zip files sent as application/zip. After adding this content type entry under .zip in the registry, I was able to download the file without a problem. But this was just to test my theory. Changing the cache-control header was the solution.
I am unsure how/if this effects I.E. on other platforms.
Harry
10-Dec-2004 12:26
Regarding IE choking on PHP-based "downloads" over SSL: problem is that you set "no-cache" and Internet Explorer interprets this to mean "never save to disk. EVER. Even if user explicitly asks for it.". It doesn't distinguish between caching stuff and user-requested save actions.
Dumb, I know. There's at least 3 or 4 Microsoft KB articles related to the issue; google "internet explorer ssl download fails" for more than you ever wanted to know.
Try removing the no-cache directive. And, if like I found, that *STILL* doesn't fix it, check to see if you are using PHP sessions anywhere - if you are, it also sets no-cache, so you need to use session_cache_limiter() to get rid of it there.
I hope this note saves someone the 2 days of hair-pulling I went through.
kbrown at fulchesterunited dot com
06-Dec-2004 03:01
Interesting one this - I have a client who is selling MP3's online. To protect his files, they are obsucrely named, with that name being stored in the Postgresql backend. When the user clicks an MP3 to download, we wrote a script that would dump the file contents and rename etc. to allow the user to download but not guess other files names.
Now, testing was done in Firefox, everything worked just ducky, loaded 'er up in IE. Nada. "File could not be cached..." error and the browser just hung waiting for the transfer to begin. After tinkering, reading these pages, trying the inline/image trick, trying all kinds of things I went on a hard search on Google. Nothing I treid seemed to work.
The following code did not work in IE:
<?
$name = addslashes(str_replace(" ", "_", $name).".mp3");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$len = filesize("/path/client/mp3/".md5("prefix_$id"));
header("Content-type: audio/mpeg;\r\n");
header("Content-Length: $len;\r\n");
$head = "Content-Disposition: attachment; filename=\"$name\";\r\n";
header($head);
$realpath = "/path/client/mp3/".md5("prefix_$id");
readfile($realpath);
?>
As a last resort, I moved the whole system off the SSL to test some other issues (server & platform)
The problem ended up being that the file download was taking place on a Secure Socket Layer. https://somedomain/download_mp3.php rendered the code invalid. Removing the s, to make http://somedomain/download_mp3.php (I know this is not the best way of maintaining security, but after paying for files, people would be pretty upset if they didn't download!) works like a charm.
I have not found out why this problem occurs yet, I've emailed Microsoft and told them about this (rather stupid) oversight on their part - who does testing over there any way!!! I am assuming that the way IE handles certificates and codec processing is not implemented correctly.
So, I hope this helps - if you're having problems forcing http headers in order to force downloads, content-types, if you're using SSL, try checking your code on non-SSL....
Eric
03-Dec-2004 08:29
I wasted an early-morning hour on this one. Although there is a cautionary note about this I think it bears emphasizing here.
I'm using an include to set some variables before I process the input from a form, then issue a 'Location' redirect.
The include file had a blank line after the final "?>" Note - It was not output (ie: NOT printed or echoed) it was simply an empty line after I closed the PHP clause.
This empty line caused the redirect to fail. Removing this empty line fixed the problem.
Just FYI. Hope it helps.
php at bucksvsbytes dot com
30-Nov-2004 06:33
After running into trouble executing
<?php
header("Location: ...");
?>
statements, I found the following advice in a posting, "You should include an exit statement after a call to header() if you want the script to stop. If you leave out the exit statement, several additional statements may be executed before the headers are sent and this can lead to unpredictable behavior."
Adopting this solved my problems, so I emphasize it as an important practice.
jamief at qubesoft dot com
24-Nov-2004 06:19
If you specify a filename with a number in a content disposition header, Internet Explorer 6 tries to be clever with it, and plays with the filename to try to version it. However, if you don't specify a filename, it will happily use the filename in the URL, and will NOT try to version it. Useful if you have filenames like Q-1.1-win32-bin.msi and you don't want them to end up as Q-1(1).1-win32-bin.msi.
Unfortunately Firefox performs similar mangling regardless of how it received the filename.
didaniman at hotmail dot com
24-Nov-2004 01:05
One little thing that slowed my development for more than a week, was the lach of \r\n characters at the end of the Content:Disposition header, as well as the application:octet-stream one.
So people, when you use these headers, please remember to use the end characters as well...
EtHeO out...
m
02-Nov-2004 04:39
To foxyshadis:
I do not think you follow Jeremy's situation -- IE gives two options when receiving "Content-Disposition: attachement" source; "Open" and "Save As". The latter prompts for a location while "Open" will save the file in its local cache (Temporary Internet Files) and open it directly from there. Jeremy presents a "sane" solution to those who experience a problem using the Open button, apperantly caused by a failure of IE to retain the just-downloaded file under certain Cache-Control settings.
Please avoid deriding other people's code to work around your own misunderstanding of the issues at hand.
guvnor
25-Oct-2004 01:38
One that tripped me up for a while...
When I use PHP sessions, the following headers are sent automatically to force the browser not to cache:
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
If you are having trouble with inline PDFs etc., thses may be causing you problems. As per other notes here if you overwrite these headers before outputting your file, the download problems will go away.
header('Cache-Control:');
header('Pragma:');
If you wish to retain the dynamic content, only send the above if the document you are returning is not HTML.
Its well worth examining your own headers (call your PHP script from another script using get_headers() for instance) before going mad trying to fix something - better still compare the headers from your script with headers from a static web page - it might save you hours of time.
alex at primafila dot net
23-Oct-2004 03:46
Note that when using the "Location:" header you must provide an absolute URI. Using relative paths such as "/index.php" could cause unusual behaviour (for example IIS processes the destination page before sending it to the client, so the client isn't actually redirected).
foxyshadis at hotmail dot com
17-Oct-2004 10:15
To Jeremy:
The reason it was downloading was because you used
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=$filename");
This is the generic "download and save me" content-type, and "Content-Disposition: attachment" is specifically intended to pop up a save as box. I'm not sure how your hacks got it to ignore that, but all you had to do was use text/plain or text/html and inline instead. Please avoid adding bad hacks to work around your misunderstanding of the relevant rfcs to the php manual.
A sane impl of your script:
<?php
$SRC_FILE = "/path/outside/web/server/test.txt";
$filename = basename($SRC_FILE);
header("Content-Type: text/plain");
header("Content-Disposition: inline; filename=$filename");
@readfile($SRC_FILE);
?>
There are additional considerations, like IE tending to ignore content-type and using the request uri for the filename.
Trust the webserver to either add the content-length or ignore it, unless you absolutely have to have pipelining for your pages.
marcelo at redcetus dot com
15-Oct-2004 07:01
Redirect the POST like this:
header("HTTP/1.0 307 Temporary redirect");
header("Location: https://myserver.redcetus.com/otherlocation");
if you dont put the 307 status code, the browser will use the GET method even if the original request was a POST
WARNING: the POST method should not be idempotent. If you need to use this, you better take a closer look to your design.
Jonas Forsman <jfo123 (- at -) hotmail dot com>
14-Oct-2004 02:14
I realized that a download script easily could be made to download the whole server in clear code. That is, all your jewels revealed. To avoid this, use a directory where only your downloadable files resides and second, strip the URL from everything that can be used as evil code.
Evil code coould look like this:
download.php?dl=../../index.php or
download.php?dl=../../password.txt
The below example protects you fairly well from these types of attacks.
$dir = $_SERVER['DOCUMENT_ROOT'].'/download/';
$file = $dir.basename($_REQUEST['dl']);
if (isset($_REQUEST['dl']) && file_exists($file) ) {
header('Content-type: application/force-download');
header('Content-Transfer-Encoding: Binary');
header('Content-length: '.filesize($file));
header('Content-disposition: attachment;
filename='.basename($file));
readfile($file);
} else {
echo 'No file with this name for download.';
}
smlsml AT g m a i l
14-Oct-2004 01:51
In IE, you must allow inline PDFs to be cached or they will not load.
Errors like:
File type: Adobe Acrobat Control for ActiveX
and
Internet Explorer was unable to open this site
Are caused by headers like:
header("Pragma: no-cache");
header("Cache-Control: no-store, no-cache");
Use the following and generate a unquie filename:
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: must-revalidate");
$pdf_file = $filename . date("ymdhis") . ".pdf";
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename=\"$pdf_file\"");
readfile("original.pdf");
dobleerre at estudiowebs dot com
05-Oct-2004 01:50
If you use the content type image/png instead of application/pdf when sending a PDF file on the header you can override the Famous IExplorer Bug ;).
$len = filesize($filename);
header('Content-type: image/jpeg');
header('Content-Length: $len');
header('Content-Disposition: inline; filename="filename.pdf"');
readfile($filename);
IExplorer tries to open the picture but because of the file-extension .pdf , voila. The acrobat reader plugin opens the file without even ask you for download.
I've read somewhere, "there is no turnarround on this bug, please install service pack 2".
There is always a turnarround :)
Have fun!!!
Raśl Raja Martinez. dobleerre@estudiowebs.com
j dot gizmo at aon dot at
28-Sep-2004 04:09
some browsers always reload stylesheets, javascripts and other seldomnly changing files, which causes nasty delays when loading a website (Safari on MacOS is an example)
to tell the browser to keep files in cache for at least a day, you can use
<?php
header('Expires: ' . gmdate('D, d M Y H:i:s', time()+24*60*60) . ' GMT');
?>
This has the nice sideeffect of telling other browser that never refresh pages to refresh them at least once a day.
PS: i figure this is trivial, but it cost me some headache
bjfield at gmail com
17-Sep-2004 09:25
After upgrading to Windows XP Service Pack 2 (which may or may not be related to this issue), my IE became unable to fetch PDF files served up by the following code:
header('Content-type: application/pdf');
readfile ($filepath);
The error was "file not found," or something to that effect. This is the solution:
header('Content-type: application/pdf');
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Length: ' . filesize($filepath));
readfile ($filepath);
Ben
ondrew at quick dot cz
17-Sep-2004 07:19
How to force browser to use already downloaded and cached file.
If you have images in DB, they will reload each time user views them. To prevent this, web server must identify each file with ID.
When sending a file, web server attaches ID of the file in header called ETag.
header("ETag: \"uniqueID\");
When requesting file, browser checks if the file was already downloaded. If cached file is found, server sends the ID with the file request to server.
Server checks if the IDs match and if they do, sends back
header("HTTP/1.1 304 Not Modified");
else
Server sends the file normally.
<?php
$file = getFileFromDB();
$hash = md5($file['contents']);
$headers = getallheaders();
if (ereg($hash, $headers['If-None-Match']))
{
header('HTTP/1.1 304 Not Modified');
}
else
{
header("ETag: \"{$hash}\"");
header("Accept-Ranges: bytes");
header("Content-Length: ".strlen($file['content']));
header("Content-Type: {$mime}");
header("Content-Disposition: inline; filename=\"{$file['filename']}\";");
echo $file['content'];
}
exit();
?>
Jeremy
10-Sep-2004 11:34
I have a download script that uses HTTP headers to download files that are located outside of the web path (on a Linux web server).
I was doing what was posted in the normal examples:
$SRC_FILE = "/path/outside/web/server/test.txt";
$download_size = filesize($SRC_FILE);
$filename = basename($SRC_FILE);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=$filename");
header("Accept-Ranges: bytes");
header("Content-Length: $download_size");
@readfile($SRC_FILE);
But in Internet Explorer, whenever I would click the link to downlad, I could only save the file. If I tried opening the file directly, it gave an error about not being able to find the temporary file.
After digging around for a while, I found the solution. You have to set the Cache-Control to private. So I added the following headers (on top/before the others):
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: private");
And now it works. I had a different Cache-Control setting before, and it wasn't working. So I'm certain that setting the Cache-Control to private is what fixed it.
The server environment is Apache 1.3.29 with PHP 4.3.8 running over mod_ssl and using PHP sessions.
Hope this helps someone!
pete at flifl dot com
20-Jul-2004 06:08
Is Unicode, UTF-8 and setcookie, session_start at the same time impossible...?
Well, then you might need this...:
1) Keep your source files in ASCII to avoid the Byte Order Mark (BOM) confusion hell when include'ing or require'ing multiple files and avoid cookies not working because of the "header already sent" thing..
2) use this source:
-------->
<?
header('Content-Type: text/html; charset=utf-8');
header('Set-Cookie: track=978268624934537');
?>
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf-8" />
<--------
Output through Apache to the browser will be UTF-8 and does not require browser to get page twice and the cookie works.
Your Chinese or cyrillic characters will work and come on out right too, provided you make an input script to put them into mysql using this scheme too.
Seems to me to be the way to use utf-8 with cookies. I hope you like it.
Peter Sierst Nielsen
smgallo at buffalo dot edu
12-Jul-2004 01:08
I've noticed many suggestions to re-order the headers in a script, especially the 'Content-Type: application/octetstream' and 'Content-Type: application/octet-stream' headers.
Note that without adding the "false" option to the header() call, you are simply overwriting all previous headers with the last one called. For example:
header("Cache-Control: must-revalidate"); // HTTP/1.1
header("Cache-Control: post-check=0, pre-check=0, max-age=0");
will return only
Cache-Control: post-check=0, pre-check=0, max-age=0
Steve
luzian dot NO at SPAM dot london dot com
14-Jun-2004 10:25
Just a reminder if you use sessions: Be careful when you want to use
header('Location: XXX');
as a redirect to XXX. In fact, previously written session variables might not be correctly written, which results in loss of these variables in the called script. So if you use sessions, ALWAYS use
session_write_close();
BEFORE a header redirect. This makes sure that the session variables previously set by $_SESSION['foo'] = bar; or session_register('foo'); are actually written to the session database before the script is terminated. So a tasty bit of code would be:
<?php
$foo = "bar"
session_register('foo');
$_SESSION['foo2'] = $foo; session_write_close(); header("Location: http://server.com/file.php"); exit(); ?>
motion at abv dot bg
22-Apr-2004 03:18
In response to Martin Anso and for your benefit, here's an even better routine for forcing the browser to save a file, rather than opening it.
-----------
$f = fopen("file.txt", "rb");
$content_len = (int) filesize($f, "file.txt");
$content_file = fread($f, $content_len);
fclose($f);
$output_file = 'something.txt';
@ob_end_clean();
@ini_set('zlib.output_compression', 'Off');
header('Pragma: public');
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate'); // HTTP/1.1
header('Cache-Control: pre-check=0, post-check=0, max-age=0'); // HTTP/1.1
header('Content-Transfer-Encoding: none');
header('Content-Type: application/octetstream; name="' . $output_file . '"'); //This should work for IE & Opera
header('Content-Type: application/octet-stream; name="' . $output_file . '"'); //This should work for the rest
header('Content-Disposition: inline; filename="' . $output_file . '"');
header("Content-length: $content_len");
echo $content_file;
exit();
php at n0spm dot bradquinn dot com
02-Feb-2004 07:07
Another solution for getting the browser to use the right filename for saving is to use mod_rewrite to hide the fact that you are using a script at all.
Either in the httpd.conf or a .htaccess file, include something like the following:
RewriteEngine On
RewriteRule ^/downloads/(.*) /getFile.php?filename=$1 [NE]
mod_rewrite will translate the url, and the browser (and the user) won't know the difference.
isaac at blueapples dot org
03-Dec-2003 02:11
[Edit: The Specification explicitely says that field names are case insensitive. That's an IE bug.]
Please refer to the HTTP/1.1 specification carefully when writting the field-name, as it is known that at least IE will fail for at least some headers if they are not capitalized as in the specification.
Example,
<?php
header("Content-type: text/plain; charset=utf-8");
header("Content-Type: text/plain; charset=utf-8");
?>
ahring.de
02-Dec-2003 12:54
After processing POST data, I usually write
header("HTTP/1.1 303 REDIRECT");
header("Location: http://$absurl");
which should be the correct method (strictly speaking, 302 for HTTP/1.0 clients)
After uploading the working scripts to our ISP the first line gave an 500 Internal Server Error.
I could not find out what triggered this error, as everything else was working fine. I have no access to the apache-error-logs nor the configfiles. phpinfo() returns apache 1.3.27 (Red-Hat), PHP 4.1.2.
Taking the hint from the documentation and changing to
header("Location: http://$absurl");
header("Status: 303");
worked for me. I don't know what circumstances made the 500 ISE happen, but if it does for you, try Status.
(since I read somewhere about an PHP bug in an old version, I also first set the Location, then the Status. Checking via Telnet to Port 80, it sets the status correctly)
jp at webgraphe dot com
21-Nov-2003 05:56
A call to session_write_close() before the statement
header("Location: URL");
exit();
is recommended if you want to be sure the session is updated before proceeding to the redirection.
We encountered a situation where the script accessed by the redirection wasn't loading the session correctly because the precedent script hadn't the time to update it (we used a database handler).
JP.
manuzhai dot REMOVE dot THIS at php dot net
18-Nov-2003 05:00
If you are using a redirect to an ErrorDocument, you may want to prefix your output with header("HTTP/1.0 200 OK"); to make sure automated clients don't think your file wasn't found.
emmett_the_spam at yahoo dot com
04-Nov-2003 07:17
This is a heads-up not just for php, but for any method of creating a 302 redirect. Mac IE 5.1.4 (osx) has a serious bug when it comes to the 302.
Say you have a form post page A with action pointing to a submit page B, and the submit page B processes and sends a 302 redirect back to the form page A. All works fine with that part. Now hit refresh while on page A, and the last form POST is suddenly delivered to page A!
This can be a very confusing bug to deal with, depending on how your code handles incoming post data. It could also be potentially very dangerous in terms of data loss, if it occurs within database administration pages (where I ran into it). What you may want to do is plan your site so that the form page itself never needs to read POST data, and then ignore all POST data. Either that, or in the location url from your header function add a query argument such as "nopost=1" which, when present, indicates to your page A code to ignore the POST data.
I've tested with Firebird Mac/PC, and IE6 on PC, and those browsers do not exhibit this behaviour.
eakolb at dygel dot net
27-Oct-2003 04:58
One of my company's clients was having login issues with the database system we created for them when they started loading the database start page (stored on a separate machine not included in the same domain) in a frameset. After quite a bit of research I found that I needed to include a Compact Privacy Policy in the webpage headers.
This can be accomplished like so:
header("P3P: CP=\"CAO DSP AND SO ON\"");
For more information on P3P and privacy policies, see the following resources:
MSDN - Privacy in Internet Explorer 6 (via TinyURL):
http://tinyurl.com/skl4
W3C - The Platform for Privacy Preferences 1.0 (P3P1.0) Specification:
http://www.w3.org/TR/P3P/
Jurgen Westerhof <php at jurgle dot nl>
27-Sep-2003 11:07
Suggested filename on download:
If you want to use the header() command for file downloads, please note that when zlib.output_compression in the php.ini is turned 'On' a filename suggested with Content-Disposition will be ignored (at least with MS IE 6.00, with Mozilla Firebird and Netscape it works fine, so far)
The workaround is:
<?PHP
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
?>
kevin at glengroup dot com
31-Jul-2003 04:44
It's worth noting that Internet Explorer (PC only, by the look of things) limits the length of a URL to 2083 characters. This makes it impossible to pass long URLs (for instance, containing url-encoded textblock form values) via the "Location" header. More can be found on this subject here:
http://support.microsoft.com/support/kb/articles/Q208/4/27.ASP
bMindful at fleetingiamge dot org
31-May-2003 05:08
If you haven't used, HTTP Response 204 can be very convenient. 204 tells the server to immediately termiante this request. This is helpful if you want a javascript (or similar) client-side function to execute a server-side function without refreshing or changing the current webpage. Great for updating database, setting global variables, etc.
header("status: 204"); (or the other call)
header("HTTP/1.0 204 No Response");
kevin at sylandro dot com
22-May-2003 02:03
If you're using PHP to generate a style-sheet file, you should use:
header("Content-Type: text/css");
Michail A.Baikov
18-Jan-2003 01:20
For correct work with IE (without Page not found Error):
header("HTTP/1.0 301 Moved Permanently");
header("Location: ".$url);
change to:
header("HTTP/1.1 301 Moved Permanently");
header("Location: ".$url);
header("Connection: close");
mpriatel at rogers dot com
23-Sep-2002 04:41
dadarden_nospamola at iti2 dot nospamola dot net
19-Jul-2002 07:38
If you use session_start() at the top of a php script that also has header() calls later in the script for a file download then you must add some form of cache control for IE to work properly. I use header('Cache-Control: public'); immediately after the code at the top of the script with the session_start() call that verifies that I have a properly logged in user. That allows the header() and fpassthru() calls to download a file later in the script using IE 5.5 SP2.
dpiper at stens dot com
23-May-2002 07:34
For inline images (JPEG for example):
header('Content-Type: image/jpeg');
header('Content-Disposition: inline; filename=file.jpg);
For attachments (Adobe PDF for example):
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename=file.pdf);
NOTE: In Internet Explorer, the Content-Disposition header is important, otherwise it will be inline. 'Content-Disposition: attachment' will ALWAYS make IE download it.
NOTE: In Netscape, if you want to force it to be a download (i.e. not inline), use header('Content-Type: application/octet-stream').
Netscape doesn't appear to care about the Content-Disposition header apart from when it's in an email message, then the header controls behaviour as expected.
It's best to be specific about the file you're sending. Don't rely on the interpretation of the browsers in the face of missing or default headers.
Content-Length is good to set for downloads, since it will allow the browser to show a progress meter. It has to be accurate otherwise the browser will stall in downloading.
| |