PHP: header - Manual

downloads | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

headers_list getservbyport

Last updated: Sat, 27 Jan 2007

view this page in

# header

(PHP 3, PHP 4, PHP 5)

header -- Send a raw HTTP header

## Description

void header ( string string [, bool replace [, int http_response_code]] )

header() is used to send a raw HTTP header. See the HTTP/1.1 specification for more information on HTTP headers.

> Замечание: Since PHP 4.4.2 and PHP 5.1.2 this function prevents more than one header to be sent at once as a protection against header injection attacks.

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:

`<?php header('WWW-Authenticate: Negotiate'); header('WWW-Authenticate: NTLM', false); ?>`

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.

`<?php header("HTTP/1.0 404 Not Found"); ?>`

> Замечание: 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.

`<?php header("Location: http://www.example.com/"); /* Redirect browser */ /* Make sure that code below does not get executed when we redirect. */ exit; ?>`

> Замечание: 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
> /* Redirect to a different page in the current directory that was requested */
> $host = $_SERVER['HTTP_HOST'];
> $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
> $extra = 'mypage.php';
> header("Location: http://$host$uri/$extra");
> exit;
> ?>`

> Замечание: Session ID is not passed with Location header even if session.use_trans_sid is enabled. It must by passed manually using `SID` constant.

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:

`<?php header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past ?>`

> Замечание: 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.

`<html>
<?php
/* This will give an error. Note the output
* above, which is before the header() call */
header('Location: http://www.example.com/');
?>`

> Замечание: 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.

`<?php
// We'll be outputting a PDF
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
// The PDF source is in original.pdf
readfile('original.pdf');
?>`

> Замечание: 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.

> Замечание: 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.

add a note User Contributed Notes
header
robert dot vogt at caflor dot net
03-Mar-2007 09:48
`To send the browser a .csv file seamlessly, I use:
header("Content-type: application/vnd.ms-excel");
header("Content-disposition: attachment; filename=$trip.csv");
This seems to work just fine in FireFox and IE7.`
rich at pioneering-principles dot co dot uk
02-Mar-2007 03:18
`I had a site where a flash swf was importing xml
The xml was parsed as php by adding
AddType application/x-httpd-php .xml to the .htaccess file of that directory.
All was working well the php was outputting the xml that the swf needed, but then we moved the site to an https domain, and internet explorer 7 stopped being able to read the xml (firefox still worked correctly).
by adding
header('Pragma: private');
header('Cache-control: private, must-revalidate');
to the xml (parsed as php) page, the problem was cured, and ie7 was able to run the swf on the https domain.`
Vince <deth AT dethsrealm DOT com>
22-Feb-2007 01:20
`I'm no experienced php programmer, in fact I started less than 4 months ago...but I've found that to open the save dialog in all browsers I've tried (firefox,navigator, ie6+). firefox and navigator are the most recent versions available as of the date of this message. I am unsure about previous versions. I do not know of any php version compatibility issues.
<?php
// set the content as octet-stream
header("Content-Type: application/octet-stream");
// tell the thing the filesize
header("Content-Length: " . filesize("filename.suffix"));
// set it as an attachment and give a file name
header('Content-Disposition: attachment; filename="filename.suffix"');
// read into the buffer
readfile("filename.suffix");
?>
this is converted a bit from code I actually use. I know my code isn't pretty but I've never really been one to follow code-cleanliness standards, although I know I should. If anyone finds any issues with this let me know please so I can correct it, but so far it works for me.
I've found that this simplified method will open the save dialog in all of the aforementioned browsers and that they will be able to display progress. I hope this has been helpful.`
22-Feb-2007 05:34
`To download anything I tried both the solutions by "Fred p" and by "info /at/ storytellermusic /dot/ nl" but neither worked.
I found that Fred P's solution worked in no browser!
And found that the info solution worked in everything but IE!
In the end I found that the solution which allows you to download any file in any browser is the one by:
Nick Sterling
25-Jul-2006 06:17
Here's his code again if you can't find it:
<?php
$mm_type="application/octet-stream";
header("Cache-Control: public, must-revalidate");
header("Pragma: hack");
header("Content-Type: " . $mm_type);
header("Content-Length: " .(string)(filesize($url)) );
header('Content-Disposition: attachment; filename="'.basename($url).'"');
header("Content-Transfer-Encoding: binary\n");
$fp = fopen($url, 'rb');
$buffer = fread($fp, filesize($url));
fclose ($fp);
print $buffer;
?>
Worked in Firefox 2.0.0.1, IE 7 and Opera 9. The downloader even knows the size of the download which isn't the case with many solutions. Cheers!`
sean at aliencreations dot com
12-Feb-2007 07:47
`I wrote a simplified function to redirect users to a secure version of the page they wish to see. Just call the function at the top of any page that requires SSL.
function secure_page()
{
if ( !isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) != 'on' )
{
header ('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
exit();
}
}`
philip.at.thedeejay.dot.dk
09-Feb-2007 01:22
`We experienced some very odd problems with an iframe setup that used header('Location:'); somewhere in the flow.
On our Mac machines (Safari, Firefox) the whole setup just stalled, but on our XP machines (IE, Firefox, Opera) it worked just fine!
The solution to the problem was to use exit(); after the header('Location:');. Don't know if this only is a problem for our setup. Just a little reminder to always use exit(); ! It will save you alot of gray hairs!!!`
guesswho at dsl dot pipex dot com
28-Jan-2007 07:14
`As far as I can tell IE 6 (and presumably earlier versions) will never cache a file that is obtained by content negotiation. For example, the MultiViews option in Apache. Either the Vary or TCN return header is causing some trigger in IE and stopping it caching the files.
This behaviour is fixed in IE 7 but I know the vast majority of users may well not be using IE 7, especially in a corporate environment.
Also I don't think you can suppress these headers from PHP since they are added by apache *after* php processes the file. You can suppress them with mod_headers but then you are interfering with the normal operation of the content negotiation and your files may inadvertantly be cached by proxy servers along the route somewhere. And even if you dont mind them being cached by a proxy server they may be served to a client that doesn't want to accept that particular content-type.`
Dave Bachtel
26-Jan-2007 09:34
`When working with .pdfs and IE6 (especially when trying to generate headers dynamically), alot of people are encountering the 'There was an error opening this document. This file cannot be found.' that the .pdf reader is throwing after being shelled from IE.
Here is a solution (tested in both Firefox and Internet Explorer 6) that seems to work for me (as of 01/26/2007):
<?php
//Fix cache control for PDFs in IE
header('Content-Disposition: attachment; filename="downloaded.pdf"');
header('Expires: 0');
header('Pragma: cache');
header('Cache-Control: private');
echo $pdf->Output("","S");
exit();
?>
It's a simple fix to an IE caching bug. Good luck!`
sarimarton at gmail dot com
23-Jan-2007 02:59
`Filenames in the browser's download dialog can be a bit problematic, when it contains spaces or non-ASCII characters. To set the appropriate cross-browser header, use the following code. It is assumed that the script receives a query like this: download.php?source=/content/példa fájl.doc
<?php
$filename = basename($_GET['source']);
$ie = eregi("msie", $_SERVER['HTTP_USER_AGENT']);
if ($ie) $filename = rawurlencode($filename);
/* ... */
header("Content-Disposition: attachment; filename=\"$filename\"");
?>`
mr.joris[at]gmail.com
08-Jan-2007 09:43
`[Quote]
Asim Siddiqui
20-Oct-2006 06:26
For those having the problem with IE adding underscores to spaces... here's a workaround:
$filename = 'test file with spaces.txt';
if(stristr($_SERVER['HTTP_USER_AGENT'],'MSIE') !== FALSE)
{
$filename = str_replace(' ','%20',$filename); // replace space with %20 so IE will read it as a space
}
header('Content-Disposition: attachment; filename="'.basename($filename).'";');
readfile($fileLocation);
[/Quote]
You can also use urldecode($string) for replacing all the charachters from de url and not only the white space "%20".
So the code will be:
$filename = urldecode($filename); // replace space with %20 so IE will read it as a space`
Hayley Watson
27-Dec-2006 05:31
`If you wish to force a file to be downloaded and saved, instead of being rendered, remember that there is no such MIME type as "application/force-download". The correct type to use in this situation is "application/octet-stream", and using anything else is merely relying on the fact that clients are supposed to ignore unrecognised MIME types and use "application/octet-stream" instead (reference: Sections 4.1.4 and 4.5.1 of RFC 2046).`
Hayley Watson
27-Dec-2006 05:15
`As an alternative to using header('Content-Type: ****') on almost every page, the default Content-Type (and character set, too, if needed) can be set in php.ini under the "default_mimetype" and "default_charset" entries.`
michal dot kocarek at seznam dot cz
14-Dec-2006 07:43
`Notice the differences between following headers
1.) <? header('Content-Disposition: inline; filename="presentation.pdf"'); ?>
2.) <? header('Content-Disposition: attachment; filename="presentation.pdf"'); ?>
The second one FORCES the browser (IE, for ex.) to show [Save File As...] dialog. In this (M$ PowerP. Presentation presentation) case, when I click on [Open] button in the dialog, it opens the program and presentation for edit).
The first one allows browser to display file "inline". In this case, presentation is displayed on the screen without opening M$ PowerP. Browser does not show [Save File As...] dialog.
Note that you need to send proper headers to browser too.
Best practice to force browser to show [Save File As...] dialog is this:
<?
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="presentation.pdf"');
?>`
13-Dec-2006 11:16
`hey david, I do the same when I want to made a "true" XHTML document.
<?php
header('Content-Type: application/xhtml+xml; charset=ISO-8859-1');
?>
Since the server automatically adds text/html to web documents.
I only use it though when I want to demonstrate why IE doesn't fully support XHTML, even IE7 doesn't.
Most of my pages I keep at text/html for the IE users to enjoy :D`
david at webdawn dot com
10-Dec-2006 08:26
`We're building an application that parses the URL and responds according to the structure contained there (similar to the way WordPress does it). The problem we ran into was that in spite of having the correct charset (ISO-8859-1) in the database and set in the HTML meta tags, the browser was still rendering the pages in UTF-8 encoding (which seems to be the default on the server). Using AddCharset in .htaccess didn't work, since that only applies to certain file extensions and was useless given our "domain.com/directory/directory/" URL style (that is, no file extensions appear in the URL, so the charset in .htaccess seems to be ignored). After searching high and low on the Web for a solution, the only thing we found that worked was using the following to force the Content-Type for displaying the pages our application generates:
<?php
header('Content-Type: text/html; charset=ISO-8859-1');
?>
This is sent to the browser before any content is output. Now all the accents on the page display correctly and the proper charset information is sent to the browser. Hopefully this will save someone else some head-scratching! If anyone knows of a better way to handle this issue (we've tried everything we came across so far), please let me know.`
H0MER
24-Nov-2006 02:34
`I think this might be useful for other people, because i searched nearly the whole web for it :)
If you need to create your own header and want to send text from a textfield with multiple lines (\r\n - Return), you will notice, that after the first \r\n (Return) your data will be truncated.
To solve this "problem" you need to replace the \r\n in the submitted string with %0D%0A.
<?php
$formatted_string = preg_replace('/(\r\n)/', '%0D%0A', $unformatted_string);
?>
I hope this will help others to avoid hours of searching :).
With kind regards
H0MER`
mailforlindsay at gmail dot com
15-Nov-2006 02:00
`Careful! This line of code cause IIS to crash on PHP 4.3.4 and maybe others.
<?
header("location:/currentfile.php");
// forward slash causes crash
// currentfile.php is the exact basename of this file
?>
Learned this the hard way.`
customizeit at pinstripe dot com
07-Nov-2006 09:43
`When messing with custom headers, it is extremely helpful to make sure that they are actually sent to the server. Nothing is more frustrating than discovering that some custom header was improperly formatted or not sent at all. There is an Internet Explorer add-in called ieHTTPHeaders that I've found to be immensely useful:
http://www.blunck.info/iehttpheaders.html
Firefox (and Mozilla) users can use an equivalent tool/plugin called LiveHTTPHeaders:
http://livehttpheaders.mozdev.org/`
jeremiah at jkjonesco dot com
30-Oct-2006 03:23
`If you are trying to modify headers to work with dynamic binaries such as videos or images, the new IE 7 appears to require the ETag header. You will need to make sure that you follow the specifications for how ETag works in order for your cache control to work properly. Mozilla supports the ETag header as well, but does NOT require it for caching. If you need to cache a dynamic image, video, or other binary file, and are having trouble in IE7, be sure to set your ETag and then check for the If-Not-Modified header on subsequent requests so that you can properly return the 304 Not Modified page. If you do not, then it will NEVER cache the file (as of IE version 7.0.5730.11`
Asim Siddiqui
20-Oct-2006 09:26
`For those having the problem with IE adding underscores to spaces... here's a workaround:
$filename = 'test file with spaces.txt';
if(stristr($_SERVER['HTTP_USER_AGENT'],'MSIE') !== FALSE)
{
$filename = str_replace(' ','%20',$filename); // replace space with %20 so IE will read it as a space
}
header('Content-Disposition: attachment; filename="'.basename($filename).'";');
readfile($fileLocation);`
kamermans at teratechnologies dot net
11-Oct-2006 05:49
`If you are trying to send image data to a mobile phone from PHP, some models (Motorola RAZOR V3 on Cingular) for whatever reason require the "Last-Modified" header or they will not show the image.
<?php
ob_start();
// assuming you have image data in $imagedata
$length = strlen($imagedata);
header('Last-Modified: '.date('r'));
header('Accept-Ranges: bytes');
header('Content-Length: '.$length);
header('Content-Type: image/jpeg');
print($imagedata);
ob_end_flush();
?>
date('r') produces the date with the numeric timezone offset (-0400) versus Apache, which uses timezone names (GMT), but according to the HTTP/1.1 RFC, dates should be formatted by RFC 1123 (RFC 822) which states: "There is a strong trend towards the use of numeric timezone indicators, and implementations SHOULD use numeric timezones instead of timezone names. However, all implementations MUST accept either notation." (http://www.ietf.org/rfc/rfc1123.txt)`
Olivier Mengué
07-Oct-2006 05:13
`If this doesn't work:
<?php
header("HTTP/1.0 404 Not Found");
?>
You should set option cgi.rfc2616_headers in php.ini.
If you can't, use this:
<?php
function header_status($status)
{
// 'cgi', 'cgi-fcgi'
if (substr(php_sapi_name(), 0, 3) == 'cgi')
header('Status: '.$status, TRUE);
else
header($_SERVER['SERVER_PROTOCOL'].' '.$status);
}
header_status('404 Not Found');
?>
See also http://bugs.php.net/bug.php?id=27345`
Fred P.
04-Oct-2006 09:46
`// This works on both Firefox and IE6
// It display the PDF properly
// in Adobe Reader embedded browser viewer
<?php
ini_set('zlib.output_compression', 'Off');
/* IE FIX : FireFox Compatible */
header('HTTP/1.1 200 OK');
header('Status: 200 OK');
header('Accept-Ranges: bytes');
header('Content-Transfer-Encoding: Binary');
header('Content-Type: application/force-download');
header('Content-Disposition: inline; filename=text.pdf');
#header('Pragma: anytextexeptno-cache', true);
#header('Cache-control: private');
#header('Expires: 0');
include_once('class.ezpdf.php');
$pdf =& new Cezpdf();
$pdf->selectFont('./fonts/Helvetica.afm');
$pdf->ezText($_GET['text'],50);
$pdf->ezStream();
exit;
?>`
support at sumago dot de
04-Oct-2006 01:46
`If you don't want people to go to
http://www.sumago.de,
but want them automatically redirected to
http://www.sumago.de
or
http://sumago.de
(so with the 'www.' or the 'prefix.'), this function might be useful:
<?
if (!strstr($_SERVER['HTTP_HOST'],"www."))
{
Header("Location:http://www.".$_SERVER['HTTP_HOST'].
"/?".$_SERVER['QUERY_STRING']);
}
?>
Replace the 'http://www.' to your own prefix. E.g. 'http://prefix.'`
alex dot pleiades at gmail dot com
02-Oct-2006 11:06
`Re downloading MP3 content. I think you had a typo with "length" and if you set the content type to 'audio/MP3' it seems to behave in IE,Firefox,SeaMonkey (Mozilla) and Opera 9
This works for me...
$fileHandle = '../'.$productCode.'.mp3'; // gets it from a spot that you can't access via a browser
header('HTTP/1.1 200 OK');
header('Date: ' . date("D M j G:i:s T Y"));
header('Last-Modified: ' . date("D M j G:i:s T Y"));
header("Content-Type: audio/mp3");
header("Content-Length: " . (string)(filesize($fileHandle)) );
header("Content-Transfer-Encoding: Binary");
header('Content-Disposition: attachment; filename="'.$productCode.'.mp3"' );
readfile($fileHandle);`
info /at/ storytellermusic /dot/ nl
29-Sep-2006 01:46
`I've been trying to force a download in IE, but everytime it displayed the MP3 data as text in my IE window.
None of the suggestions made below worked for me.
I've based the headers on the download of a normal .GIF file. Here's mine, it's very basic and seems to work on both IE and FireFox.
<?php
header('HTTP/1.1 200 OK');
header('Date: ' . date("D M j G:i:s T Y"));
header('Last-Modified: ' . date("D M j G:i:s T Y"));
header("Content-Type: application/force-download"); // changed to force download
header("Content-Lenght: " . (string)(filesize($filelocation)));
header("Content-Transfer-Encoding: Binary"); // added
header("Content-Disposition: attachment; // added
filename=".str_replace(" ", "", basename($filelocation)).""); // added to remove spaces in filename when saving, it seemed to cause some problems with spaces.
readfile($filelocation);
?>`
Michael J. Hudson
27-Sep-2006 09:12
`There has been some discussion on the best way to FORCE a
refresh of a page. In general, the solutions revolve on
reducing or eliminating caching. However, I think that what
some people are looking for... is how does one force a real
refresh of the page after some internal action on the page
(be it a javascript action OR a button was pressed). I found
the following code VERY helpful when I encountered this
need. Specifically, I had a listbox where one could press a
button to delete a selected entry in that listbox. The listbox
was dynamically linked to the contents of an external source
only in the sense that the listbox got updated with the
current list ONLY when the page got refreshed. Thus, I
needed a way to force a refresh after the person pressed the
delete button. I did this by adding the following code after
my actual delete action.
<?php
header( "Location: http" .
(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']
== "on" ? "s" : "") . "://" .
$_SERVER['SERVER_NAME'] . ":" . $_SERVER['SERVER_PORT'] .
( isset($_SERVER['REQUEST_URI']) ? $_SERVER
['REQUEST_URI'] .
( $_SERVER['QUERY_STRING'] ? "?" . $_SERVER
['QUERY_STRING'] : "" ) : "" ) );
?>
Do note, the above code is written to be VERY generic.
You can probably simplify it greatly if the port is a standard
port and/or you are always using http or https and/or
you don't expect to have a query string appended to the end.
BTW, I didn't come up this code myself... I happened across
it while reading the following article on auto-login techniques
for Mediawiki:
http://meta.wikimedia.org/wiki/
User:Otheus/Auto_Login_via_REMOTE_USER/code`
digideath at web dot de
24-Aug-2006 12:38
`Hello,
i have found a working method for IE Cache BUG, it's not the best but it is working on all IE's in our Company..
i send only the http/1.x 205 OK header additionaly to the rest of the anti cache bug header things (;
and it is working! (:
<?php
//[HTTP_USER_AGENT] => Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
if(!strpos(strtolower($_SERVER[HTTP_USER_AGENT]), "msie") === FALSE)
{
header("HTTP/1.x 205 OK");
} else {
header("HTTP/1.x 200 OK");
}
header("Pragma: no-cache");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Datum aus Vergangenheit
//header("Expires: -1");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // immer geändert
//header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1
header("Cache-Control: no-cache, cachehack=".time());
header("Cache-Control: no-store, must-revalidate");
header("Cache-Control: post-check=-1, pre-check=-1", false);
?>`
aooa83(a)dsldotpipexdotcom
19-Aug-2006 02:26
`apache_request_headers() is only available if PHP is running as an apache module. Various request header values are available in the $_SERVER array, for example:
$_SERVER["HTTP_IF_MODIFIED_SINCE"]
Gives the if modified date in "Sat, 12 Aug 2006 19:12:08 GMT" format.`
admin at commoncents dot net dot au
05-Aug-2006 09:00
`Thanks for everyones help with this guy's,
I am using session_start and need to have files available for users to download whilst ensuring that only users who are logged in can download the files.
The other priority is that I needed to make sure that users cannot type something like download.php?dl=index.php or ../index.php
I have taken the best points from everything above and now use:
<?php
include "config.php"; config.php contains the session information
header('Cache-Control: public');
if ( $_SESSION["logged_in"] == 1){
$dir="downloadfolder/";
$file=$dir.$_GET["dl"];
if (isset($_REQUEST['dl']) && file_exists($file) ) {
header('Pragma: anytextexeptno-cache', true);
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.';
}
}else{
echo "You are not logged in<br>";
}
?>`
yomi at hotmail dot co dot uk
27-Jul-2006 03:45
`IE download bug issue
header('Pragma: private');
header('Cache-control: private, must-revalidate');
This worked for me, I have been having this problem for some weeks with IE download bug problem, a quick copy and paste of the 2 lines into my script from the user notes did the trick for me`
devonmitton at gmail dot com
26-Jul-2006 01:38
`I ran into the problem of losing the session after i redirect the user with header( location:);
I found that the problem doesn't occur in php5. Atleast for myself. I tested it on IIS as well as Apache and both times using php5 fixed the issue.
Secondly, I found a weird solution: The website i'm building required Cyrillic type, so i encode the pages using UTF-8. The problem that happens is that UTF-8 sends information before the php tags, and therefore before the session_start(); which ultimately renders the session_start(); call ineffective.
The UTF-8 encoding sends a small chunk of information before anything. This is called BOM (byte order marks).
To ensure that they don't show up, you need to save your files with a UTF-8 encoding which bypasses BOM. BBEdit for the Macintosh with allow for this under the file encoding options.
To recap: In this instance and for my situation, UTF-8, no BOM will allow sessions to pass through the header( location:); redirect, or using PHP5.
Hope this helps someone! [i will also repeat this comment under the session_start() function]`
Nick Sterling
25-Jul-2006 11:17
`Internet Explorer could not find the temporary file it was trying to download, so after many hours of searching, this is what I have come up with (and it's working)...
<?php
header("Cache-Control: public, must-revalidate");
header("Pragma: hack");
header("Content-Type: " . $mime_type);
header("Content-Length: " .(string)(filesize($downloadfile)) );
header('Content-Disposition: attachment; filename="'.basename($downloadfile).'"');
header("Content-Transfer-Encoding: binary\n");
$fp = fopen($downloadfile, 'rb');
$buffer = fread($fp, filesize($downloadfile));
fclose ($fp);
print $buffer;
?>`
pulstar at ig dot com dot br
17-Jul-2006 06:25
`There is a very good page about browsers caching and PHP at:
http://www.sitepoint.com/article/php-anthology-2-5-caching
I'm sure it will be helpful.`
spongebob3 at hotmail dot com
03-Jun-2006 04:47
`If you don't want people to go to
http://example.com/index.php?id=5,
but want them automatically redirected to
http://www.example.com/index.php?id=5
or
http://prefix.example.com/index.php?id=5
(so with the 'www.' or the 'prefix.'), this function might be useful:
<?
if (!strstr($_SERVER['HTTP_HOST'],"www."))
{
Header("Location:http://www.".$_SERVER['HTTP_HOST'].
"/?".$_SERVER['QUERY_STRING']);
}
?>
Replace the 'http://www.' to your own prefix. E.g. 'http://prefix.'.`
dotmg at wikkàwiki dot ORG
25-May-2006 11:40
`<?php header('Content-Length: ', $size); ?>
is not a so good idea. If the server has session.use_transient_sid enabled, you cannot know the correct size of output sent to client 'coz urls will be suffixed with strings like '?PHPSESSID=somemd5hash'. If you don't use this header, you have no problem because PHP will use Transfer-encoding: chuncked.
<?php header('Vary: Content-language'); ?>
may be ignored if you use ob_gzhandler.`
jherman at digitalgravy dot net
17-May-2006 02:16
`PHP as CGI treats header differently.
header("HTTP/1.0 404 Not Found"); returns "404 ok"
When using PHP (3,4 and 5) as CGI to return a 404 you need to use:
header("Status: 404 Not Found"); this returns "404 Not Found"
See: http://bugs.php.net/bug.php?id=27345`
pechkin at zeos dot net
05-May-2006 06:00
`I've created script that gives users ability to download files from closed directory. It supports multithread download and download resuming
<?php
$fname = $_GET['file'];
$fpath = "downloads/$fname";
$fsize = filesize($fpath);
$bufsize = 20000;
if(isset($_SERVER['HTTP_RANGE'])) //Partial download
{
if(preg_match("/^bytes=(\\d+)-(\\d*)$/", $_SERVER['HTTP_RANGE'], $matches)) { //parsing Range header
$from = $matches[1];
$to = $matches[2];
if(empty($to))
{
$to = $fsize - 1; // -1 because end byte is included
//(From HTTP protocol:
// 'The last-byte-pos value gives the byte-offset of the last byte in the range; that is, the byte positions specified are inclusive')
}
$content_size = $to - $from + 1;
header("HTTP/1.1 206 Partial Content");
header("Content-Range: $from-$to/$fsize");
header("Content-Length: $content_size");
header("Content-Type: application/force-download");
header("Content-Disposition: attachment; filename=$fname");
header("Content-Transfer-Encoding: binary");
if(file_exists($fpath) && $fh = fopen($fpath, "rb"))
{
fseek($fh, $from);
$cur_pos = ftell($fh);
while($cur_pos !== FALSE && ftell($fh) + $bufsize < $to+1)
{
$buffer = fread($fh, $bufsize);
print $buffer;
$cur_pos = ftell($fh);
}
$buffer = fread($fh, $to+1 - $cur_pos);
print $buffer;
fclose($fh);
}
else
{
header("HTTP/1.1 404 Not Found");
exit;
}
}
else
{
header("HTTP/1.1 500 Internal Server Error");
exit;
}
}
else // Usual download
{
header("HTTP/1.1 200 OK");
header("Content-Length: $fsize");
header("Content-Type: application/force-download");
header("Content-Disposition: attachment; filename=$fname");
header("Content-Transfer-Encoding: binary");
if(file_exists($fpath) && $fh = fopen($fpath, "rb")){
while($buf = fread($fh, $bufsize))
print $buf;
fclose($fh);
}
else
{
header("HTTP/1.1 404 Not Found");
}
}
?>`
cameron at prolifique dot com
04-May-2006 01:24
`I've had several people ask me about my multi-featured custom 404 page (see post on 11-Jun-2005 below) and even request the full code for it. So here it is:
www.prolifique.com/404.php.txt
I've tried to clean it up to be self-contained (rather than rely on copious other include files) and to remove any extraneous functionality.
Works on both Windows and Apache servers.
Enjoy. There may be mistakes from the clean-up; feel free to mention them. Suggestions for improvements welcome.`
abrikos dot org
03-May-2006 09:38
`<?
if(strstr($_SERVER[HTTP_USER_AGENT],"MSIE"))
{
$fname=str_replace("+"," ",urlencode($fname));
}
header("Content-Disposition: attachment; filename=\"$fname\"");
?>
this code help to set valid names of attachment for IE users`
php.net[at]macosbrain[dot]com
30-Apr-2006 02:19
`at first one advise to Jon's post (09-Mar-2006 01:38)
the "function downloadFile ($file, $mimetype)" can NOT resume downloads.
the function sent every time the same file just with different header informations.
this function will work
<?php
function output_file($file,$name)
{
//do something on download abort/finish
//register_shutdown_function( 'function_name' );
if(!file_exists($file))
die('file not exist!');
$size = filesize($file);
$name = rawurldecode($name);
if (ereg('Opera(/| )([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
$UserBrowser = "Opera";
elseif (ereg('MSIE ([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
$UserBrowser = "IE";
else
$UserBrowser = '';
/// important for download im most browser
$mime_type = ($UserBrowser == 'IE' || $UserBrowser == 'Opera') ?
'application/octetstream' : 'application/octet-stream';
@ob_end_clean(); /// decrease cpu usage extreme
header('Content-Type: ' . $mime_type);
header('Content-Disposition: attachment; filename="'.$name.'"');
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header('Accept-Ranges: bytes');
header("Cache-control: private");
header('Pragma: private');
///// multipart-download and resume-download
if(isset($_SERVER['HTTP_RANGE']))
{
list($a, $range) = explode("=",$_SERVER['HTTP_RANGE']);
str_replace($range, "-", $range);
$size2 = $size-1;
$new_length = $size-$range;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range$size2/$size");
}
else
{
$size2=$size-1;
header("Content-Length: ".$size);
}
$chunksize = 1*(1024*1024);
$this->bytes_send = 0;
if ($file = fopen($file, 'r'))
{
if(isset($_SERVER['HTTP_RANGE']))
fseek($file, $range);
while(!feof($file) and (connection_status()==0))
{
$buffer = fread($file, $chunksize);
print($buffer);//echo($buffer); // is also possible
flush();
$this->bytes_send += strlen($buffer);
//sleep(1);//// decrease download speed
}
fclose($file);
}
else
die('error can not open file');
if(isset($new_length))
$size = $new_length;
die();
}
?>
http://macosbrain.ath.cx/wordpress/2006/04/30/
for some comments in german.`
hervard at gmail dot com
17-Apr-2006 11:53
`When using HTML forms, using the browser's back button will sometimes display a message regarding using cached data, and will ask you to refresh the page. This can be very disconcerting for some users, as they might not know whether to hit Refresh or not.
To force pages to always load the data that was entered in the form prior to hitting a submit button, and prevent the browser's cache message from displaying, use the following code:
<?php
// Original code found at http://www.mnot.net/cache_docs/
header("Cache-Control: must-revalidate");
$offset = 60 * 60 * 24 * -1;
$ExpStr = "Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT";
header($ExpStr);
?>
This will tell the browser that the page will expire in one day, and the cached form data will be used without prompting the user at all.
I have tested this in Internet Explorer 6, Firefox 1.5, and Opera 8.51, and it works as intended. I have tried other cache-control and expiry variants, but they either do not work, or do not work in every browser. This code appears to be a winner.`
nath at dbpixels dot net
14-Apr-2006 12:47
`not worked out a way round yet, but just to let you all know that the following code messes up IE2 beta 2 and causes pages to load blank... Any of you who use this and have clients who like to keep up with the latest technologies may want to remove, edit swiftly..
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
Cheers all`
bkatz at usefulengineering dot com
09-Apr-2006 03:46
`When doing a simple redirect using header, i.e.:
header("Location: login.php");
you may want to place a command to terminate your script following this, like exit:
header("Location: login.php");
exit();
Otherwise, you may find, unexpectedly, that your script following the redirect continues execution. Not sure if this is expected behavior, but regardless, in the case of, for instance, a redirect to a login page when a user is not logged in, that's definitely something you'll want to add to prevent "secured" code from running.`
shawnfess at comcast dot net
08-Apr-2006 02:53
`Internet Explorer will not accept certain filenames in the filename attribute of the Content-Disposition header. It will Sit 'N Spin until it finally times out. The following will not work when $fn contains "Web", "Browser" or "WebBrowser" (or any case variation thereof):
$fn = $row["sFilename"];
$fs = $row["lFileSize"];
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=$fn");
header("Content-length: $fs");
echo $row["blobData"];
Guess I should have looked here first. Yes, it does seem we're all searching for workarounds for IE.
-SHAWN-`
gnoise at gmail dot com
02-Apr-2006 11:21
`A small note on the header('Content-Disposition: attachment; filename="downloaded.pdf"') -usage:
I found out that the use of double-quotes (") around the filename doesn't work in Firefox, and is not needed for the header to work, even for filenames with spaces in it.
So use this instead:
<?
header('Content-Disposition: attachment; filename=filename spaces are ok.pdf')
?>`
nick dot b at kingstransport dot com dot au
22-Mar-2006 06:57
`Constructing an absolute URL (for redirecting or other purposes) is full of pitfalls. As well as considering the notes at http://www.php.net/manual/en/function.header.php#63006 and http://www.php.net/manual/en/function.header.php#61746 you need to consider this issue, applicable if you use ProxyPass (in apache).
Example httpd.conf
...
ProxyPass /dir/ http: //10.1.1.1/dir/
...
Member of public ---> Unix/Apache server ---> IIS/PHP server
http: //nick.com/ nick.com myphpsvr.nick.com
dir/phpinfo.php 20.10.5.1 10.1.1.1
When your script is on the IIS/PHP server, browsing from member of public, you will have script variables
<?php
// browsing from member of public
$_SERVER['HTTP_HOST'] == '10.1.1.6';
$_SERVER['HTTP_X_FORWARDED_HOST'] == 'nick.com';
$_SERVER['SERVER_NAME'] == '10.1.1.6';
?>
However, if you browse internally (like, when testing, http: //myphpsrv.nick.com/dir/phpinfo.php), the variables turn out like this
<?php
// browsing from internal, direct
$_SERVER['HTTP_HOST'] == 'myphpsvr.nick.com';
$_SERVER['SERVER_NAME'] == 'myphpsvr.nick.com';
// no $_SERVER['HTTP_X_FORWARDED_HOST']
?>
The point is, you need to test $_SERVER['HTTP_X_FORWARDED_HOST'] and use that in preference to either $_SERVER['HTTP_HOST'] or $_SERVER['SERVER_NAME'].
Nick Bishop.
URL's have intentionally been broken up to stop them being clickable.`
steve at stevedix dot de
21-Mar-2006 05:39
`Referring to the infamous Microsoft Explorer bug, particularly when outputting PDF : The problem is this -
Most well-behaved browsers require a mime-type before they attempt to display anything. If they don't receive a mime-type from the server, then they revert to text/plain.
Not Microsoft Explorer.
Microsoft Explorer attempts to ascertain what type of file it is recieving by looking in the file first. You can test this by renaming a graphic file so that it does not have a .extension (ie rename test.gif to be test), and upload it to your server. Look at it via a URL, and any compliant browser will render it as text. Not Microsoft Explorer, which renders the file as a graphic. Unfortunately, this behavior is not consistent.
The really big problem comes with the Adobe postscript plugin. Most browsers will shut up and listen when given a mime type for PDF. Not Microsoft Explorer. It sees that the url generating the file contains a .php, and gets confused.
I eventually discovered that the best way to stop this problem was to generate the pdf to a file, move it to a subdirectory under the htdocs, and then issue a redirect to it. Examples of this method can be seen in use at www.onvista.de, such as
http://fonds.onvista.de/snapshot.html?ID_INSTRUMENT=6628444
Which launches a separate window, generates the pdf, and then redirects to it.
Naughty, naughty Microsoft browser. No biscuit for you.`
vaughan montgomery
10-Mar-2006 05:57
`i was rackin my brains trying to figure out this IE bug..
when you hit the button to start the download, in IE you get the dialogue box asking you whether you want to save the file to disk or open it. non of the methods on this page seemed to work.
after a lot of experimenting i found a solution which works in IE and firefox etc. i don't know about mac as i don't own one.
the solution i used is >
$mimeType = $download->getVar('filetype');
$filePath = '<your path to file>/'.trim($filename);
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-Transfer-Encoding: binary");
if(isset($mimeType))
{
header("Content-Type: " . $mimeType);
}
header("Content-Disposition: attachment; filename=" . $filename);
if(isset($mimeType) && strstr($mimeType, "text/"))
{
$fp = fopen($filePath, "r");
}
else
{
$fp = fopen($filePath, "rb");
}
fpassthru($fp);
exit();
exit() is also required.
note. you maybe asking why Content-Length is not added, that is because for some reason adding content-length stopped the actual file downloading correctly or opening. i don't know why, but as it worked without, i wasn't worried about not having it.
please note tho, in the script, $download->getVar('filetype'); is a value that is pulled from a DB, the submission part of the script stores the mimetype in the db aswell, it was easier to do it that way, so adjust mimetype to whatever you require b4 you use it..
even pdf files download and also open aswell from the browser dialogue box in IE.`
nino at clan-csc dot com
10-Mar-2006 09:32
`There is actually a way to allow header() function after something.
Open your php.ini file and change the line
output_buffering = Off
to
output_buffering = On
this allows you to send headers no matter what.
But then you'll get an perfomance lowering.
So if perfomance matters alot to you, do this:
output_buffering = 4096
It is a REALLY small perfomance lowering, and you can send some header() 's.
-Nino`
Jon
09-Mar-2006 01:38
`Seems like we are all trying to get round this IE bug (Apache 2 + PHP Server (Solaris) / IE6), after going through the solutions here I've not been that sucessful - also yet to solve the https problem. The solution I've adopted for http is a modified version of the following link:
http://www.phpbuilder.com/board/showthread.php?threadid=10318152
Jon
function downloadFile ($file, $mimetype)
{
$status = 0;
if (($file != NULL) && file_exists($file)) {
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: ' . mime_content_type($file)
header ('Content-type: ' . $mimetype);
header ('Content-Disposition: attachment; filename="'.basename($file).'"');
header ('Expires: '.gmdate("D, d M Y H:i:s", mktime(date("H")+2, date("i"), date("s"), date("m"), date("d"), date("Y"))).' GMT');
header ('Accept-Ranges: bytes');
// Use Cache-control: private not following:
// header ('Cache-control: no-cache, must-revalidate');
header("Cache-control: private");
header ('Pragma: private');
$size = filesize($file);
if(isset($_SERVER['HTTP_RANGE'])) {
list($a, $range) = explode("=",$_SERVER['HTTP_RANGE']);
//if yes, download missing part
str_replace($range, "-", $range);
$size2 = $size-1;
$new_length = $size2-$range;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range$size2/$size");
}
else
{
$size2=$size-1;
header("Content-Range: bytes 0-$size2/$size");
header("Content-Length: ".$size);
}
if ($file = fopen($file, 'r')) {
while(!feof($file) and (connection_status()==0)) {
print(fread($file, 1024*8));
flush();
}
$status = (connection_status() == 0);
fclose($file);
}
}
return($status);
}
function downloadStream(&$stream, $filename, $mimetype)
{
$status = 0;
$size = strlen($stream);
if ($size > 0) {
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: ' . mime_content_type($file)
header ('Content-type: ' . $mimetype);
header ('Content-Disposition: attachment; filename="' . $filename . '"');
header ('Expires: '.gmdate("D, d M Y H:i:s", mktime(date("H")+2, date("i"), date("s"), date("m"), date("d"), date("Y"))).' GMT');
header ('Accept-Ranges: bytes');
// Use Cache-control: private not following:
// header ('Cache-control: no-cache, must-revalidate');
header("Cache-control: private");
header ('Pragma: private');
if(isset($_SERVER['HTTP_RANGE'])) {
list($a, $range) = explode("=",$_SERVER['HTTP_RANGE']);
//if yes, download missing part
str_replace($range, "-", $range);
$size2 = $size-1;
$new_length = $size2-$range;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range$size2/$size");
}
else
{
$size2=$size-1;
header("Content-Range: bytes 0-$size2/$size");
header("Content-Length: ".$size);
}
// Dump the content.
echo $stream;
$status = 1;
}
return($status);
}`
marc.giroux at pointpubmedia dot com
07-Mar-2006 01:58
`i tried alot of ways to pass the IE bug and i finally stumbled upon 1 and here it is.. it work very well for me .. and it is IE/FireFox compatible
/* Force Download Function *IE WORKING* */
function force_download($filename) {
$filesize = filesize($filename);
if($filesize) {
ini_set('zlib.output_compression', 'Off');
/* IE FIX : FireFox Compatible */
header("Content-Type: application/force-download\n");
header("Content-Disposition: attachment; filename=$filename");
/* Read It */
$contents = fread(fopen($filename, "rb"), filesize($filename));
/* Print It */
echo $contents;
exit;
} else {
return 0;
}
}`
kim at bonfils dot com
07-Mar-2006 01:31
`In response to fig dot miguel at gmail dot com:
Please be aware that the "Refresh" header is not part of the official HTTP specification. Most browsers (like IE and Firefox) seem to support it, but I've run into problems with Safari (apparently, it adds an extra blank space to the URL, making it unusable).
So don't rely on the "Refresh" header. If you use it, make sure you test it on all relevant browsers.`
cameron at prolifique dot com
28-Feb-2006 11:36
`One further note about forcing downloads in Internet Explorer.
If the server has not yet issued an HTTP status code or has issued something other than a 200, you need to send HTTP/1.1 200 OK to the browser so that IE accepts the download without an error:
header("HTTP/1.1 200 OK");
and/or
header("Status: 200 OK");
I use a custom 404 error page to handle secure downloads. The user clicks on a link to a file such as 'http://mysite.com/user_files/file.txt'. That file does not exist, so my 404 page is invoked. It parses the address and if it finds the 'user_files' folder in the address, it attempts to open the file 'file.txt' from a non-web-accessible folder and sends it as a download instead of showing the 404 message.
The problem I had was that IE always showed this error when you attempted to download or open the file:
"Internet Explorer cannot download file.txt from mysite.com. Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found. Please try again later." The download worked fine in FireFox, and strangely, even worked fine on my local test server (Windows XP) in IE. But on the remote server (Apache) in IE, it threw the error above until I added the above status header(s). Now the code reads:
header("HTTP/1.1 200 OK");
header("Status: 200 OK");
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="'.$downloadFile.'"');
header('Content-Length: '.filesize($downloadFilePath));
readfile($downloadFilePath);`
einavb
27-Feb-2006 03:54
`For all of you IE SSL desperados,
All the listed download tweaks did not work for me,
until I removed the "?" sign for the query string (IE SSL cache "feature").`
me at macronesia dot net
21-Feb-2006 06:46
`Since PHP 4.4.2, you'll need to have header(); on each line rather than header(" ... \r\n" . " .... \r\n");`
mandor at mandor dot net
14-Feb-2006 05:14
`When using PHP to output an image, it won't be cached by the client so if you don't want them to download the image each time they reload the page, you will need to emulate part of the HTTP protocol.
Here's how:
<?php
// Test image.
$fn = '/test/foo.png';
// Getting headers sent by the client.
$headers = apache_request_headers();
// Checking if the client is validating his cache and if it is current.
if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == filemtime($fn))) {
// Client's cache IS current, so we just respond '304 Not Modified'.
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($fn)).' GMT', true, 304);
} else {
// Image not cached or cache outdated, we respond '200 OK' and output the image.
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($fn)).' GMT', true, 200);
header('Content-Length: '.filesize($fn));
header('Content-Type: image/png');
print file_get_contents($fn);
}
?>
That way foo.png will be properly cached by the client and you'll save bandwith. :)`
timmerca.com
14-Feb-2006 08:07
`I store a lot of images in databases rather than files, and PHP was causing my user's web browsers to not cache these images, which means that every time they went to my web site, they were reloading the same images over and over again. To fix this, I have added the following headers to my download.php script:
Header("Expires: " . date("D, j M Y H:i:s", time() + (86400 * 30)) . " UTC");
Header("Cache-Control: Public");
Header("Pragma: Public");
This causes the user's web browsers to cache the image data (in this case for 30 days, but you can make that whatever you want). This sped up my web sites and cut down on hits to my server considerably. So, if you are delivering images through PHP, you should consider adding these headers to your script too.`
fig dot miguel at gmail dot com
10-Feb-2006 09:42
`A function to redirect, using different approaches. The destination page can include a full URL, a full path or a local path.
<?
function g_redirect($url,$mode)
/* It redirects to a page specified by "$url".
* $mode can be:
* LOCATION: Redirect via Header "Location".
* REFRESH: Redirect via Header "Refresh".
* META: Redirect via HTML META tag
* JS: Redirect via JavaScript command
*/
{
if (strncmp('http:',$url,5) && strncmp('https:',$url,6)) {
$starturl = ($_SERVER["HTTPS"] == 'on' ? 'https' : 'http') . '://'.
(empty($_SERVER['HTTP_HOST'])? $_SERVER['SERVER_NAME'] :
$_SERVER['HTTP_HOST']);
if ($url[0] != '/') $starturl .= dirname($_SERVER['PHP_SELF']).'/';
$url = "$starturl$url";
}
switch($mode) {
case 'LOCATION':
if (headers_sent()) exit("Headers already sent. Can not redirect to $url");
header("Location: $url");
exit;
case 'REFRESH':
if (headers_sent()) exit("Headers already sent. Can not redirect to $url");
header("Refresh: 0; URL=\"$url\"");
exit;
case 'META':
?><meta http-equiv="refresh" content="0;url=<?=$url?>" /><?
exit;
default: /* -- Java Script */
?><script type="text/javascript">
window.location.href='<?=$url?>';
</script><?
}
exit;
}
?>`
vsnake at email dot ru
05-Feb-2006 10:51
`in situation when I use SSL, sessions and send file to user technique, MSIE dont get a file.
This code don't work:
session_start();
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="file.pdf"');
header('Content-Length: ' . filesize($fileName));
readfile($fileName);
And I get a solution:
session_start();
header('Pragma: anytextexeptno-cache', true);
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="file.pdf"');
header('Content-Length: ' . filesize($fileName));
readfile($fileName);
The point is: replace header 'Pragma: no-cache'
Good luck!`
spingary at yahoo dot com
12-Jan-2006 12:53
`I was having trouble with streaming inline PDf's using PHP 5.0.2, Apache 2.0.54.
This is my code:
<?
header("Pragma: public");
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");
header("Content-type: application/pdf");
header("Content-Length: ".filesize($file));
header("Content-disposition: inline; filename=$file");
header("Accept-Ranges: ".filesize($file));
readfile($file);
exit();
?>
It would work fine in Mozilla Firefox (1.0.7) but with IE (6.0.2800.1106) it would not bring up the Adobe Reader plugin and instead ask me to save it or open it as a PHP file.
Oddly enough, I turned off ZLib.compression and it started working. I guess the compression is confusing IE. I tried leaving out the content-length header thinking maybe it was unmatched filesize (uncompressed number vs actual received compressed size), but then without it it screws up Firefox too.
What I ended up doing was disabling Zlib compression for the PDF output pages using ini_set:
<?
ini_set('zlib.output_compression','Off');
?>
Maybe this will help someone. Will post over in the PDF section as well.`
Ciantic
23-Dec-2005 06:07
`This is a bit extended the idea what Per Eckerdal had. Note that this contains all HTTP status responses defined in RFC2616 section 6.1.1 http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1
<?PHP
/**
* HTTP Protocol defined status codes
* @param int $num
*/
function HTTPStatus($num) {
static $http = array (
100 => "HTTP/1.1 100 Continue",
101 => "HTTP/1.1 101 Switching Protocols",
200 => "HTTP/1.1 200 OK",
201 => "HTTP/1.1 201 Created",
202 => "HTTP/1.1 202 Accepted",
203 => "HTTP/1.1 203 Non-Authoritative Information",
204 => "HTTP/1.1 204 No Content",
205 => "HTTP/1.1 205 Reset Content",
206 => "HTTP/1.1 206 Partial Content",
300 => "HTTP/1.1 300 Multiple Choices",
301 => "HTTP/1.1 301 Moved Permanently",
302 => "HTTP/1.1 302 Found",
303 => "HTTP/1.1 303 See Other",
304 => "HTTP/1.1 304 Not Modified",
305 => "HTTP/1.1 305 Use Proxy",
307 => "HTTP/1.1 307 Temporary Redirect",
400 => "HTTP/1.1 400 Bad Request",
401 => "HTTP/1.1 401 Unauthorized",
402 => "HTTP/1.1 402 Payment Required",
403 => "HTTP/1.1 403 Forbidden",
404 => "HTTP/1.1 404 Not Found",
405 => "HTTP/1.1 405 Method Not Allowed",
406 => "HTTP/1.1 406 Not Acceptable",
407 => "HTTP/1.1 407 Proxy Authentication Required",
408 => "HTTP/1.1 408 Request Time-out",
409 => "HTTP/1.1 409 Conflict",
410 => "HTTP/1.1 410 Gone",
411 => "HTTP/1.1 411 Length Required",
412 => "HTTP/1.1 412 Precondition Failed",
413 => "HTTP/1.1 413 Request Entity Too Large",
414 => "HTTP/1.1 414 Request-URI Too Large",
415 => "HTTP/1.1 415 Unsupported Media Type",
416 => "HTTP/1.1 416 Requested range not satisfiable",
417 => "HTTP/1.1 417 Expectation Failed",
500 => "HTTP/1.1 500 Internal Server Error",
501 => "HTTP/1.1 501 Not Implemented",
502 => "HTTP/1.1 502 Bad Gateway",
503 => "HTTP/1.1 503 Service Unavailable",
504 => "HTTP/1.1 504 Gateway Time-out"
);
header($http[$num]);
}
?>`
cory at lavacube dot com
16-Nov-2005 08:26
`Using apache and mod_vhost_alias, you have the oportunity to produce some amazing websites. Take this for example:
Using mod_vhost_alias, you can have apache point to your index.php file... Doing so, you can use PHP to control EVERY aspect of your website, including making "human readable paths" (mysite.com/info/contact_us/) very easily.
This is done with the Apache/mod_vhost_alias directive "VirtualDocumentRoot".
(eg: VirtualDocumentRoot "/www/mysite.com/index.php")
However, this makes it harder to display images and documents/files you would normally have static. You will require a page to actually handle the request and load the appropriate file... A simple example:
Assume URL is: http://mysite.com/images/firefox_button.png
<?php
$path = './images/';
$image = preg_replace('/^\/images\//i', null, $_SERVER['REQUEST_URI']);
$image = str_replace('..', null, $image);
$filename = $path . $image;
$file_info = pathinfo($filename);
$extn = $file_info['extension'];
if( file_exists($filename) )
{
header('Content-Type: image/'.$extn);
$open = fopen($filename, 'r');
fpassthru($open);
fclose($open);
}
else
{
header('HTTP/1.0 404 Not Found');
}
exit();
?>
The fun thing about this, is you can also supply something like this to control a cache of these images... This will greatly lighten the load on your server:
Before:
header('Content-Type: image/'.$extn);
Add:
header('Content-Control: cache');
header('Content-Type: image/'. date('r', time()+604800)); // expire in 7 days
With VirtualDocumentRoot, you can do almost _anything_...
Some prime examples where using VirtualDocumentRoot would be helpful:
- create, handle and manage 'human readable' URLs
- potentially thwart "leeching" of media, using sessions/cookies(eg: require a hit to a specific page before any images can be loaded, etc.)
- a "mod_speling" handling system
- create a "wiki" style site with ease
- and many, many others... just use your imagination
Enjoy. :-)`
brian at pjcservices dot com
27-Oct-2005 10:10
`For Internet Explorer:
When using an attachement Content-Dispositon, it seems the following headers are also required:
header("Cache-Control: cache, must-revalidate");
header("Pragma: public");
If you have Cache-Control as no-cache, IE fails.
I have done several tests and those two lines fixed my script. (It was trying to download a .csv)
The above seems to be especially true on https connections.`
hsmcdonald at gmail dot com
21-Oct-2005 10:39
`I had a special situation where I wanted to have a "Download" button on a PHP script, and have the the script post to itself to call a special block of code to initiate download of dynamically created content (rather than display contents of a file).
I was running in circles trying to get IE SP1, SP2 and Firefox on Win98, 2000, and XP to all work where the download prompt is displayed AND the filename is controlled by me and not the browser. I found the following headers from a form that seem to work in all cases.:
<?php
// load content into var
$filecontent="Some text/code im creating in the script";
$downloadfile="somefile.txt";
header("Content-disposition: attachment; filename=$downloadfile");
header("Content-Type: application/force-download");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".strlen($filecontent));
header("Pragma: no-cache");
header("Expires: 0");
?>
Hope this helps someone..`
wraezor at gmail dot com
20-Oct-2005 08:08
`Adding onto all the helpful material here, I have something to add, discovered by trial and error.
When initiating a file download with the following command:
header('Content-Disposition: attachment; filename="downloaded.pdf"');
Ensure that the 'filename' does not contain a directory path. It will work, but Internet Explorer will not pick up the filename.
Example:
I had a script, called save.php which did just that, grabbing individual files in a subdirectory ("results/downloaded.pdf"). Since it was in a subdirectory, it could find the file, but IE (Using 6sp1) wouldn't pick up the name when suggesting for the 'Save' option. Firefox and Opera were fine.
So, moral of the story, the 'filename' should only ever contain a filename, no directories. This is only really relevant when people couldn't be expected to know the filename/extension, like in my case with FRF files. (Note: This was my experience at least.)`
me at macronesia dot net
20-Oct-2005 05:27
`Make sure you place Content-Type in front of Content-Disposition or else you will run into problems!
It will also save you an hour of head scratching.`
http://pye.dyndns.org
19-Oct-2005 03:18
`The example by digibluez at gmail dot com, 06-Sep-2005 08:12 contains an error, the 'charset' part of the 'Content-Type' header should be written with an '=' sign, so instead:
<?php
header("Content-type: text/css; charset=UTF-8");
?>`
denilson at vialink dot com dot br
11-Oct-2005 04:24
`If you want to enable caching of your page, you have two solutions: use Etag or use Last-Modified.
Someone has already posted here the code to use Etag. However, sometimes, it is easier (better) to use Last-Modified. The full code is here:
<?php
function get_http_mdate()
{
return gmdate("D, d M Y H:i:s",filemtime($SCRIPT_FILENAME))." GMT";
}
function check_modified_header()
{
// This function is based on code from http://ontosys.com/php/cache.html
$headers=apache_request_headers();
$if_modified_since=preg_replace('/;.*$/', '', $headers['If-Modified-Since']);
if(!$if_modified_since)
return;
$gmtime=get_http_mdate();
if ($if_modified_since == $gmtime) {
header("HTTP/1.1 304 Not Modified");
exit;
}
}
check_modified_header();
header("Last-Modified: ".get_http_mdate());
?>
The script checks if time from "If-Modified-Since" header is equal to current modified-time. If it is, a 304 code is returned, and PHP exits. If it is not, PHP continues normally.
You may want to change how "get_http_mdate()" function gets the time. You may want to get time from another file, or from somewhere else (like a date field on database).`
memoimyself at yahoo dot com dot br
08-Aug-2005 08:01
`PROBLEM: File downloads work fine with browsers other than Internet Explorer, but when IE is used, the following error message is issued: "Internet Explorer cannot download file from server. Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found. Please try again later."
PROBABLE CAUSE: File is being downloaded over a secure connection (SSL, a.k.a. HTTPS protocol) and IE has received a "no-cache" header (as also mentioned by Harry and james at charity dot org). More info on the problem: http://support.microsoft.com/default.aspx?scid=kb;en-us;316431
SOLUTION: The following have worked for me:
header('Pragma: private');
header('Cache-control: private, must-revalidate');
or simply
header('Pragma: ');
header('Cache-control: ');
(The first option is probably safer, but that's just a guess.)`
boris at psyonline dot ru
05-Aug-2005 04:37
`On some servers IE tries to use the script name rather than the supplied filename in the Content-Disposition header. But it works correctly with unknown Content-Encoding! :-)
<?php
header("Content-Encoding: AnyTrash");
header("Content-Disposition: attachment; filename=download.txt");
echo "Content";
?>`
Luther Blisset
08-Jul-2005 09:57
`For a detailed list of HTTP status codes see:
http://www.w3.org/Protocols/HTTP/1.1/spec.html#Status-Codes
http://www.w3.org/Protocols/HTTP/1.0/spec.html#Status-Codes`
cameron at prolifique dot com
11-Jun-2005 12:29
`I use a custom PHP 404 page that does several things:
� Checks the database for a "virtual" page matching the missing page address, and if found, includes and displays it, issuing an HTTP 200 OK status. The client browser receives the page contents and never even knows that the page doesn't actually exist on the server.
� Checks the database for a redirect from this missing page address to another address, and if found, does the redirect by issuing an HTTP 302 Object Moved status and Location header.
� Tries to fix common address errors, such as removing periods or extra slashes at the end of the address, and then checks for a physical page with a matching address, a virtual page with a matching address, or a redirect, and takes the appropriate action.
� If all of the above fail, it simply shows a friendly "Page Not Found" error.
� In the case of redirects and genuine missing pages, the missing page address (and the number of hits on it) is logged to the database. In the case of genuine missing pages, an email is also sent to me so I can identify and fix broken links.
The "virtual" page behavior is perhaps the most useful, as it allows me to create totally dynamic pages on the server but use static addresses for them.
However, on one particular server on which I used this system, my HTTP 200 header wasn't getting sent correctly:
<?php
header("HTTP/1.1 200 OK");
?>
Instead, the browser received a 404, and even if the virtual page content was included and sent correctly (it was), IE showed its typical 404 error page instead based on the status code (but only if "show friendly HTTP error messages" is checked, which is the default).
Finally found this note in an older or alternate version of the PHP manual (at http://academ.hvcc.edu/~kantopet/php/manual/function.header.html):
Note: In PHP 3, this only works when PHP is compiled as an Apache module. You can achieve the same effect using the Status header.
<?php
header("Status: 404 Not Found");
?>
Though our server uses PHP/4.3.10, I thought this was worth a try, so I tried issuing both commands:
<?php
header("HTTP/1.1 200 OK");
header("Status: 200 OK");
?>
And, in fact, it worked.
So if you find that using header("HTTP/1.1 ###") isn't working, try the header("Status: ###") instead or as well.`
tylerd gmail
21-May-2005 02:19
`I was sick of the cookie domain handling issues presented by my situation:
www.foo.com/phpBB2/ - my forum
forum.foo.com - vhost to www.foo.com/phpBB2/
forums.foo.com - vhost to www.foo.com/phpBB2/
So to force all users to use the vhost I prefer (forum.*) while still passing on the entire URI (like if they were linked to a post) I added this to phpBB2/includes/page_header.php:
<?php
if($_SERVER['HTTP_HOST'] != 'forum.foo.com')
{
if(!strstr($_SERVER['REQUEST_URI'], "phpBB2")) { header("Location: http://forum.foo.com" . $_SERVER['REQUEST_URI']); }
else { header("Location: http://forum.foo.com" . substr($_SERVER['REQUEST_URI'], 7)); }
}
// yadda yadda yadda
?>
I'm sure this would work for many other situations.`
com dot gmail at schaefer dot peter
09-May-2005 11: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 :-/`
TJ
04-May-2005 03: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();`
pornel at despammed dot com
28-Apr-2005 08: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.`
mjs15451 at hotmail dot com
22-Apr-2005 12: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.`
james at charity dot org
05-Apr-2005 09: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`
snagnever at gmail dot com
31-Mar-2005 01: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
/* start of the script */
$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.";
}
/* end of the script */
?>
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']) ){
/* the script outputs the headers and the gz file content, as show_image.php script does */
} else {
echo "Your browser does not support gzip compression. Update it.";
}
?>`
markus dot amsler at oribi dot org
14-Mar-2005 09: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.`
fedenuche at gamail dot com
10-Mar-2005 08: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.`
ninj at chello dot fr
03-Mar-2005 07: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).`
jukkissh at hotmail dot com
25-Feb-2005 03: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.`
nickesh at o2 dot pl
21-Feb-2005 12: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.`
aarondunlap.com
28-Dec-2004 02: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){
//First, see if the file exists
if (!is_file($file)) { die("<b>404 File not found!</b>"); }
//Gather relevent info about file
$len = filesize($file);
$filename = basename($file);
$file_extension = strtolower(substr(strrchr($filename,"."),1));
//This will set the Content-Type to the appropriate setting for the file
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;
//The following are for extensions that shouldn't be downloaded (sensitive stuff, like php files)
case "php":
case "htm":
case "html":
case "txt": die("<b>Cannot be used for ". $file_extension ." files!</b>"); break;
default: $ctype="application/force-download";
}
//Begin writing headers
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");
//Use the switch-generated Content-Type
header("Content-Type: $ctype");
//Force the download
$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 01: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.
<?
// this works
header("Content-type: text/plain");
header("Cache-Control: no-store, no-cache");
echo($data);
// this does not work
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
09-Dec-2004 10: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 01: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:
<?
// append .mp3 onto he file name
$name = addslashes(str_replace(" ", "_", $name).".mp3");
// make sure this thing doesn't cache
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");
// create the necessary MP3 headers, this will type the page as MP3 and force download
$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);
// hash the id to create the correct path
$realpath = "/path/client/mp3/".md5("prefix_$id");
// spit out the contents of the MP3 file to the browser
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....`
jamief at qubesoft dot com
24-Nov-2004 04: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.`
guvnor
25-Oct-2004 11: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.`
marcelo at redcetus dot com
15-Oct-2004 05: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 12: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
13-Oct-2004 11: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
04-Oct-2004 11: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 02: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 07: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 05: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();
// generate unique ID
$hash = md5($file['contents']);
$headers = getallheaders();
// if Browser sent ID, we check if they match
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 09: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 04: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 11: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`
motion at abv dot bg
22-Apr-2004 01: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();`
jp at webgraphe dot com
21-Nov-2003 03: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 03: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 05: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.`
bMindful at fleetingiamge dot org
31-May-2003 03: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");`
mpriatel at rogers dot com
23-Sep-2002 02:41
`Another general fix for many of the problems that occure when trying to prompt a filename to download is to append a '/' to your download URL. This fixes a NS7 problem where it wants to add a .PHP extension to all filenames.
For example, change:
http://www.url.com/download.php?file=23
to
http://www.url.com/download.php/?file=23`
dadarden_nospamola at iti2 dot nospamola dot net
19-Jul-2002 05: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 05: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.`
add a note
headers_list getservbyport

Last updated: Sat, 27 Jan 2007
show source | credits | stats | sitemap | contact | advertising | mirror sites
Copyright © 2001-2007 The PHP Group
All rights reserved.
This mirror generously provided by: Yahoo! Inc.
Last updated: Sat Mar 10 09:32:46 2007 PST