I have written a PHP page which reads a file and does echo it after adding some headers:
header('Content-disposition: filename="' . $fname . '"');
header('Pragma: no-cache');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("Content-type: $AttachFileType");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($ffullname));
echo file_get_contents($ffullname);
This piece of code does well on local test, but when porting to the server, the response is not as expected. For example when I want to retrieve a png file, I get the below image on local test:
while server test outputs this one:
As I have investigated, the only difference of them is 4 additional headers on server response. Local test response headers:
Cache-Control: must-revalidate, post-check=0, pre-check=0
Connection: Keep-Alive
Content-disposition: filename="attachment_hrmstotal_generalskills_6.png"
Content-Length: 2401
Content-Transfer-Encoding: binary
Content-Type: png
Date: Wed, 23 May 2018 04:45:02 GMT
Expires: 0
Keep-Alive: timeout=5, max=97
Pragma: public
Server: Apache/2.4.27 (Ubuntu)
and server response headers are these:
Cache-Control: must-revalidate, post-check=0, pre-check=0
Connection: keep-alive
Content-disposition: filename="attachment_hrmstotal_generalskills_1.png"
Content-Length: 184450
Content-Transfer-Encoding: binary
Content-Type: png
Date: Wed, 23 May 2018 04:40:56 GMT
Expires: 0
Pragma: public
Server: nginx/1.8.0
Strict-Transport-Security: max-age=31536000
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
The last four headers is the difference. What is the problem, its reason and how to solve it?
Your content-type should be Content-type: image/png
Related
So I have this at the very top of my PHP file:
<?php
// Set headers
header('Content-Type: text/html; charset=UTF-8');
header('Content-Style-Type: text/css');
header('Content-Script-Type: application/javascript');
header('HTTP/1.1 200 OK');
header('Content-language: en-US');
header('X-Powered-By: ');
header_remove('X-Powered-By');
header('Last-Modified: Tue, 01 Jan 2013 00:00:00 GMT');
header('Cache-Control: no-store, no-cache, max-age=0, must-revalidate');
header('Pragma: no-store, no-cache, max-age=0, must-revalidate');
header('Expires: Tue, 01 Jan 2013 00:00:00 GMT');
?>
So when I check the Firefox console's NET tab on my site's URL I don't see the Last-Modified header. Any suggestions?
You're telling the browser to cache nothing and he MUST revalidate everything. So the last-modified header doesn't matter.
I've created a script that lets the user download files:
function file_size($filename)
{
exec('stat -c %s ' . escapeshellarg($filename), $return);
return (float)$return[0];
}
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . basename($filename));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . file_size($filename));
readfile($filename);
exit;
Very simple. The file_size function lets me detect file sizes larger than 2 GB.
The problem is that Content-length never is above 2 GB:
< HTTP/1.1 200 OK
< Date: Sun, 21 Aug 2011 09:33:20 GMT
< Server: Apache
< Content-Description: File Transfer
< Content-Disposition: attachment; filename=very-large-file
< Content-Transfer-Encoding: binary
< Expires: 0
< Cache-Control: must-revalidate, post-check=0, pre-check=0
< Pragma: public
< Content-Length: 2147483647
< Content-Type: application/octet-stream
Doing a var_dump on 'Content-Length: ' . file_size($filename) returns string(26) "Content-Length: 4689218232". If I access the file directly without a PHP script, there is no problem and Apache is reporting the correct file size:
< HTTP/1.1 200 OK
< Date: Sun, 21 Aug 2011 09:58:33 GMT
< Server: Apache
< Last-Modified: Thu, 06 Jan 2011 21:56:47 GMT
< ETag: "8ba8f5e0-1177fcab8-49934940b30e5"
< Accept-Ranges: bytes
< Content-Length: 4689218232
But I'd really like to serve the file through my PHP script. Thank you for your time.
Sending huge files with readfile is not good practice.
Use X-Sendfile, like this: header("X-Sendfile: $filename");. You'll need apache mod_sendfile. This should also solve your file-size problem.
could anyone help me with setting up Expire Headers using PHP only,
.htaccess is no good, because my host won't enable mod_expires on apache.
So basically I'm looking for a way to do:
Expire Header
<FilesMatch "\.(ico|jpg|jpeg|png|gif|js|css|swf)$">
ExpiresDefault "access plus 365 days"
</FilesMatch>
with php only.
its also important to have different expire periods for different filetypes, so I tried using something like:
header ("content-type: image/jpg; charset: UTF-8");
header ("cache-control: must-revalidate");
$offset = 48 * 60 * 60;
$expire = "expires: " . gmdate ("D, d M Y H:i:s", time() + $offset) . " GMT";
header ($expire);
for each type of files, but nothing happened.
the headers after adding the PHP code, and taken from private session:
Response Headersview source
Date Mon, 25 Apr 2011 19:47:10 GMT
Server Apache/2.2.14 (Unix) DAV/2 mod_ssl/2.2.14 OpenSSL/0.9.8l PHP/5.3.1 mod_apreq2-20090110/2.7.1 mod_perl/2.0.4 Perl/v5.10.1
X-Powered-By PHP/5.3.1
P3P CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"
Expires Mon, 1 Jan 2001 00:00:00 GMT
Cache-Control no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Last-Modified Mon, 25 Apr 2011 19:47:10 GMT
Content-Encoding gzip
Pragma no-cache
Keep-Alive timeout=5, max=100
Connection Keep-Alive
Transfer-Encoding chunked
Content-Type text/html; charset=utf-8
Request Headersview source
Host localhost
User-Agent Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.16) Gecko/20110323 Ubuntu/10.10 (maverick) Firefox/3.6.16 ( .NET CLR 3.5.30729) FirePHP/0.5
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language en-us,en;q=0.7,he;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Connection keep-alive
Referer http://localhost/-----------------
Cookie fboard_settings[current_view]=flat; style_cookie=null; phpbb3_4s1go_k=; phpbb3_4s1go_u=2; phpbb3_4s1go_sid=8a3835a63834e9851b0cde3e2f6cff63; jw_clean_pro_tpl=jw_clean_pro; acpSearchCookie[searchphrase]=any; acpSearchCookie[acpSearch]=%D7%97%D7%A4%D7%A9+...; acpSearchCookie[cid]=0; acpSearchCookie[field_city]=0; 14a2bb08766d6180968b7925b7902d70=bgd3h1uj5dctoevtdiaj1jtmg6; 3e2fd857422e2463a01f9631f718017a=nbdjbmjsn9ee8ng90ui816hec2
x-insight activate
According to your comment in the question it looks like your system is running with a PHP setting of session.cache_limiter = nocache. This would automatically send the following headers:
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Only the Expires header you mention differs a bit (afaik Joomla uses that very datetime, if i'm not mistaken). But all in all it makes no difference, because both dates are in the past.
You should try with session_cache_limiter(false); in your code, to stop PHP sending its default caching headers:
function sendHeader($sType, $iLastModified, $iSecondsToCache)
{
$aType = array(
'ico' => 'image/x-icon',
'jpg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'js' => 'text/javascript',
'css' => 'text/css',
'swf' => 'application/x-shockwave-flash'
);
if (!isset($aType[$sType]))
die('No mime type found for ' . $sType);
//$sLastModified = gmdate('r', $iLastModified);
$sLastModified = gmdate('D, d M Y H:i:s', $iLastModified) . ' GMT';
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
{
if ($_SERVER['HTTP_IF_MODIFIED_SINCE'] == $sLastModified)
{
header('HTTP/1.1 304 Not Modified');
exit;
}
}
session_cache_limiter(false);
//header('Expires: ' . gmdate('r', $iLastModified + $iSecondsToCache));
header('Expires: ' . gmdate('D, d M Y H:i:s', $iLastModified + $iSecondsToCache) . ' GMT');
header('Cache-Control: public');
header('Last-Modified: ' . $sLastModified);
header('Content-Type: ' . $aType[$sType]);
}
date_default_timezone_set('Europe/Berlin');
$iLastModified = strtotime('2011-04-25 07:08:09');
$iSecondsToCache = 48 * 60 * 60;
sendHeader('jpg', $iLastModified, $iSecondsToCache);
// stream sample image/jpeg content
$rGD = imagecreatetruecolor(100, 20);
$iColor = imagecolorallocate($rGD, 255, 255, 255);
imagestring($rGD, 1, 5, 5, 'Image to be cached', $iColor);
imagejpeg($rGD);
imagedestroy($rGD);
exit;
EDIT:
Meanwhile it's more probable to me, that Joomla is causing the problem. If the test code has access to Joomla libraries, try inserting:
jimport('joomla.environment.response');
JResponse::allowCache(true);
at the very top of the function and replace each header instruction with JResponse::setHeader.
That helps me for ajax queries:
header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' );
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s',time()+60*60*8 ) . ' GMT' );
header( 'Cache-Control: no-store, no-cache, must-revalidate' );
header( 'Cache-Control: post-check=0, pre-check=0', false );
header( 'Pragma: no-cache' );
I have this code set up that lets a user download a file through my server from a URL they specify. The file streams through using readfile() so it only uses my bandwidth.
<?php
set_time_limit(0);
$urlParts = explode("/", $_SERVER['PHP_SELF']);
$file = $urlParts[3];
header("Cache-Control: public, must-revalidate");
header("Pragma: hack");
header("Content-Type: application/force-download");
header('Content-Disposition: attachment; filename=' . $file);
header("Content-Transfer-Encoding: binary\n");
readfile($file);
?>
This script works, but it does not change the CRC hash of the downloaded file. What I want it to do is append some random bits to the end of the file so it can change the hash without corrupting it. I have tried adding something like echo md5(rand() . time()); to the end of the script but it doesn't work.
If this is possible with something like cURL I'd appreciate if someone could put up some code samples, because i'd switch to cURL if this was possible.
Thanks for your help.
Hmm, your code works for me:
test.php:
set_time_limit(0);
$urlParts = explode("/", $_SERVER['PHP_SELF']);
//$file = $urlParts[3];
$file = 'toread.txt';
header("Cache-Control: public, must-revalidate");
header("Pragma: hack");
header("Content-Type: application/force-download");
header('Content-Disposition: attachment; filename=' . $file);
header("Content-Transfer-Encoding: binary\n");
readfile($file);
echo md5(rand() . time());
?>
toread.txt:
This is the content of toread.txt
Now using curl, I get the following results:
>curl -i http://example.com/test.php
HTTP/1.1 200 OK
Date: Tue, 04 Mar 2014 07:09:39 GMT
Server: Apache
Cache-Control: public, must-revalidate
Pragma: hack
Content-Disposition: attachment; filename=toread.txt
Content-Transfer-Encoding: binary
Transfer-Encoding: chunked
Content-Type: application/force-download
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
Age: 0
This is the content of toread.txt38d8a8009fad7315bdf5e823a06018e7
And the second one:
>curl -i http://example.com/test.php
HTTP/1.1 200 OK
Date: Tue, 04 Mar 2014 07:09:57 GMT
Server: Apache
Cache-Control: public, must-revalidate
Pragma: hack
Content-Disposition: attachment; filename=toread.txt
Content-Transfer-Encoding: binary
Transfer-Encoding: chunked
Content-Type: application/force-download
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
Age: 0
This is the content of toread.txt3b87356ea9ee007b70cfd619e31da950
Here is my issue. I am trying to call a page: foo.php?docID=bar and return a PDF to the screen which is stored as a BLOB in the DB.
Here is the portion of my code which actually returns the PDF:
$docID = isset($_REQUEST['docID']) ? $_REQUEST['docID'] : null;
if ($docID == null){
die("Document ID was not given.");
}
$results = getDocumentResults($docID);
if (verifyUser($user, $results['ProductId'])){
header('Content-type: application/pdf');
// this is the BLOB data from the results.
print $results[1];
}
else{
die('You are not allowed to view this document.');
}
This works perfectly fine in Firefox.
However, in IE, it doesn't show anything at all. If i'm on another page (i.e. google.com), and I type in the URL to go to this page, it will say it's done, but I will still have google.com on my screen.
I checked the headers for the responses from both firefox and IE. They are identical.
Does anyone have any suggestions? Need more information?
EDIT: If it helps at all, here's the response header and the first line of the content:
HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 349930
Content-Type: application/pdf
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: PHP/5.1.2
Set-Cookie: PHPSESSID=cql3n3oc13crv3r46h2q04dvq4; path=/; domain=.example.com
Content-Disposition: inline; filename='downloadedFile.pdf'
X-Powered-By: ASP.NET
Date: Tue, 21 Apr 2009 16:35:59 GMT
%PDF-1.4
EDIT: Also, the page which pulls out the pdf file actually uses HTTPS instead of HTTP.
Thanks in advance,
~Zack
I figured out what the issue was. It's an IE bug dealing with IE, HTTPS and addons. (See here)
It was a caching issue. When I set:
header("Cache-Control: max-age=1");
header("Pragma: public");
(see here), the PDF was in cache long enough for the adobe reader add-on to grab it.
I had this issue too, i used the following which seems to work fine
header("Content-type: application/pdf");
header("Content-Length: $length");
header("Content-Disposition: inline; filename='$filename'");
Try this:
header("Content-Type: application/pdf");
header("Content-Disposition: inline; filename=foo.pdf");
header("Accept-Ranges: bytes");
header("Content-Length: $len");
header("Expires: 0");
header("Cache-Control: private");
Also, if you are using sessions, you can try setting
session_cache_limiter("none");
or
session_cache_limiter("private");
if ( USR_BROWSER_AGENT == 'IE' ) {
header( 'Content-Disposition: inline; filename="' . $name . '"');
header( 'Expires: 0' );
header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
header( 'Pragma: public' );
} else {
header( 'Content-Disposition: attachment; filename="' . $name . '"' );
header( 'Expires: 0' );
header( 'Pragma: no-cache' );
}
This was the only header I needed to change:
header("Pragma: public");
I think you need to add more headers.
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Disposition: attachment; filename=THEFILENAME.pdf;");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($results[1]));