 |
header (PHP 3, PHP 4, PHP 5) header -- 发送一个原始 HTTP 标头 说明void header ( string string [, bool replace [, int http_response_code]] )
header() 函数用来发送一个原始 HTTP
标头。有关 HTTP 标头的更多内容见
HTTP/1.1 规范。
可选参数 replace
指明是替换掉前一条类似的标头还是增加一条相同类型的标头。默认为替换,但如果将其设为
FALSE 则可以强制发送多个同类标头。例如:
第二个可选参数 http_response_code 强制将 HTTP
响应代码设为指定值(此参数是 PHP 4.3.0 新加的)。
有两种特殊的 header 调用。第一种是标头以字符串“HTTP/”(大小写不重要)开头的,可以用来确定要发送的
HTTP 状态码。例如,如果配置了 Apache 用 PHP
来处理找不到文件的错误处理请求(使用
ErrorDocument 指令),需要确保脚本产生了正确的状态码。
注:
HTTP 状态码标头行总是第一个被发送到客户端,而并不管实际的
header() 调用是否是第一个。除非
HTTP 标头已经发送出去,任何时候都可以通过用新的状态行调用
header() 函数来覆盖原先的。
第二种特殊情况是以“Location:”标头。它不只是把这个标头发送回浏览器,它还将一个
REDIRECT(302)状态码返回给浏览器,除非之前已经发出了某个
3xx 状态码。
注:
HTTP/1.1 标准需要一个绝对地址的 URI 做为
Location:
的参数, 但有一些客户端支持相对 URI。通常可以使用
$_SERVER['HTTP_HOST']、$_SERVER['PHP_SELF']
及 dirname() 函数来自己从相对 URI 产生出绝对 URI:
PHP 脚本通常会产生一些动态内容,这些内容必须不被浏览器或代理服务器缓存。很多代理服务器和浏览器都可以被下面的方法禁止缓存:
注:
可能会发现即使不输出上面所有的代码,网页也没有被缓冲。用户有很多选项可以设置来改变浏览器的默认缓存行为。通过发送上述标头,应该可以覆盖任何可以导致脚本页面被缓存的设置。
另外,当使用了 session 时,利用 session_cache_limiter() 函数和
session.cache_limiter 选项可以用来自动产生正确的缓存相关标头。
要记住 header()
必须在任何实际输出之前调用,不论是来自普通的 HTML 标记,空行或者
PHP。有一个常见错误就是在通过
include(),require()
或一些其它的文件存取类函数读取代码时,有一些空格或者空行在调用
header() 之前被发送了出去。同样在一个单独的
PHP/HTML 文件中这个错误也很普遍。
注:
自 PHP 4 起,可以通过一些输出缓冲函数来解决这个问题。代价是把所有向浏览器的输出都缓存在服务器,直到下命令发送它们。可以在代码中使用
ob_start() 及 ob_end_flush()
来实现这样的功能,或者通过修改 php.ini 中的 output_buffering
配置选项来实现,也可以通过修改服务器配置文件来实现。
如果想提示用户保存所发送的数据,例如一个生成的 PDF
文件,可以通过发送 Content-Disposition
标头提供推荐的文件名来强制浏览器弹出一个保存文件对话框。
注:
Microsoft Internet Explorer 4.01
中的一个漏洞使得该机制无法正常工作,无解决方案。在
Microsoft Internet Explorer 5.5
中也有个漏洞影响到这一点,升级到 Service Pack 2 或更高版本可以解决。
注:
在安全模式下,如果设定了
WWW-Authenticate 标头(用于 HTTP
认证)则脚本的 UID 会添加到其中的 realm 部分中去。
参见 headers_sent(),setcookie()
和 HTTP 认证一章。
jeremiah at jkjonesco dot com
31-Oct-2006 07: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
21-Oct-2006 12: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
12-Oct-2006 08: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
08-Oct-2006 08: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.
05-Oct-2006 12: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;
?>
alex dot pleiades at gmail dot com
03-Oct-2006 02: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 04: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
28-Sep-2006 12: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 03: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 gendert
//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 05: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
06-Aug-2006 12: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 06: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 04: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
26-Jul-2006 02: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;
?>
dotmg at wikkwiki dot ORG
26-May-2006 02: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
18-May-2006 05: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 09: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
05-May-2006 04: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
04-May-2006 12: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 05: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
18-Apr-2006 02: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 03: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 06: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
09-Apr-2006 05: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
03-Apr-2006 02: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
23-Mar-2006 10: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 09: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
11-Mar-2006 09: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
11-Mar-2006 01: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
10-Mar-2006 05: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
08-Mar-2006 05: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 05: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
01-Mar-2006 03: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
28-Feb-2006 07: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
22-Feb-2006 10: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
15-Feb-2006 09: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
15-Feb-2006 12: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
11-Feb-2006 01: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
06-Feb-2006 02: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
13-Jan-2006 04: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
24-Dec-2005 10: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
17-Nov-2005 12: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
28-Oct-2005 01: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.
|