What is removing my headers in PHP/Apache2? - php

I'm using PHP 5.3 and Apache 2.0 to serve a script which adds a number of headers to the output:
header('HTTP/1.1 200 OK');
header("Content-Type: application/json");
header("Last-Modified: $lastmode"); // $lastmod = Tue, 01 Mar 2011 14:56:22 +0000
header("Etag: $etag"); // Etag = 5da02274fcad09a55f4d74467f66a864
Now, all the headers come through except for the Last-Modified and Etag. In my httpd.conf I have the following:
Header unset Cache-Control
Header unset Pragma
But in my response I get:
HTTP/1.1 200 OK
Date: Tue, 01 Mar 2011 16:49:10 GMT
Server: Apache/2.2.14 (EL) mod_ssl/2.2.14 OpenSSL/0.9.8e-fips-rhel5
Keep-Alive: timeout=15, max=8000
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/json
Expires: 0
Cache-Control: no-cache
My Last-Modified and Etag headers are gone and we have Cache-Control and Expires in their place.
I should also mention that I have disabled mod_expires to no avail. I am pulling my hair out at this point as no matter what I do the headers are simply not there. What could possibly cause them to be removed?
Thanks,
J
UPDATE: It seems that Apache is adding the additional headers after PHP has shutdown and I would think it's also removing the headers I set above. Registering a shutdown function in PHP and calling apache_response_headers shows:
Pragma=
Expires=
Etag=5da02274fcad09a55f4d74467f66a864
Last-Modified=Tue, 01 Mar 2011 14:56:22 +0000
Keep-Alive=timeout=15, max=8000
Connection=Keep-Alive
Transfer-Encoding=chunked
Content-Type=application/json

To answer my own question it seems that the tool I was using to debug was giving me the grief. It was the mod_expires module causing the problem in the first place but the reason that removing it had no effect was that the proxy I was using to debug the problem (Charles) seems to modify the headers. Once Charles was taken out of the loop my headers were there!

Related

Why won't browsers cache files output by PHP readfile()?

I am currently outputting some image files via PHP's readfile() using the following code but, I notice via Firefox and Chrome's dev tools that none of these files get cached.
ob_start();
outputfile($fp);
function outputfile( $fp ) {
header("Content-Type: $mime_type");
header("Content-Length: " . filesize($fp));
header("Cache-Control: public, max-age=3600");
header("Etag: " . md5_file($fp));
$date = gmdate("D, j M Y H:i:s", filemtime($fp))." GMT";
header("Last-Modified: $date");
readfile($fp);
exit; // tried ob_end_flush() too before exiting
}
The code outputs the file with the following in the headers in the dev tools...
Cache-Control: public, max-age=2678400
Connection: keep-alive
Content-Length: 155576
Content-Type: image/jpeg
Date: Mon, 21 May 2018 22:31:02 GMT
Last-Modified: Sat, 03 Mar 2018 19:34:05 GMT
Etag: 507f2520385c009a7385a1165032bd61
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Server: nginx
If I return control to Nginx to serve the file instead, it outputs the following headers:
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 155576
Content-Type: image/jpeg
Date: Mon, 21 May 2018 22:31:02 GMT
ETag: "5a9af8ad-4a5b"
Last-Modified: Sat, 03 Mar 2018 19:34:05 GMT
Server: nginx
Am I missing something that causes the browsers to not cache the image files?
I've tried adding all the necessary Cache-Control headers such as eTag and max-age but, the browsers just refuses to cache the data. I even tried copying all the headers from the server's output and using "ob_start('ob_gzhandler');" in case it was because the raw file data wasn't gzipped.
The browsers just won't cache any file data sent through PHP.
Expires: Thu, 19 Nov 1981 08:52:00 GMT could be the cause. Technically if the Cache-Control header has a max-age directive then Expires should be ignored (Ref : https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires). However it is worth checking by removing that header.
Your PHP code is not setting it. So I assume it is coming from some common config / code which gets executed on every outgoing response. Have you put this in your Nginx config for all PHP requests?
I think I've found the problem...
Was wondering if any cookie related code could affect readfile() and discovered that if I had session_start() before using the function, browsers will refuse to cache the file data sent. If I remove session_start() the browser caching works as expected respecting the Cache-Control headers sent.
I don't quite understand why this is the case since I compared the output of readfile() with and without session_start() before it and the output seems to be the same.
For the record I'm using PHP 5.5.

Joomla Not Sending Custom Header

So I've got a server to server application. The PHP script on server 1, domain 1 sets a custom header in the page (Authorization: Bearer 123456789). The script on server 2, domain 2 uses get_headers() to read the headers.
It all works fine when the files are served natively. But when the script on server 1 is included in a Joomla module get_headers() doesn't retrieve the custom header.
In both cases, developer tools shows the custom header but also some different headers than returned by get_headers().
The code below uses JFactory to set the headers if Joomla is loaded but it is the same result using header(). Joomla just isn't passing the custom header.
I don't get it. Anyone have any idea what is going on here? Its not a SEF or htaccess issue.
<?php
// Server 1
if(!class_exists("JFactory")){ // no Joomla
header('Authorization: Bearer 123456789');
} else { // Joomla framework loaded
$app = JFactory::getApplication();
$app->setHeader('Authorization: ', 'Bearer 123456789');
$app->sendHeaders();
}
The code on server 2:
<?php
// Server 2
$headers = get_headers("http://server1.com/");
foreach($headers as $header) {
echo $header ."<br/>";
}
Output from get_headers() when served natively:
HTTP/1.1 200 OK
Date: Thu, 19 Jan 2017 12:44:35 GMT
Server: Apache
Authorization: Bearer 123456789
Content-Length: 0
Connection: close
Content-Type: text/html
Output from get_headers() when served by Joomla:
HTTP/1.1 200 OK
Date: Thu, 19 Jan 2017 12:45:49 GMT
Server: Apache
Set-Cookie: 3c460b3da9ecb202e794816b4144c6ff=ja7mn4b4njov98lsv76kk8pvu2; path=/; HttpOnly
Vary: Accept-Encoding
Content-Length: 1264
Connection: close
Content-Type: text/html
Native headers displayed by developer tools:
Authorization: Bearer 123456789
Date: Thu, 19 Jan 2017 13:07:32 GMT
Server: Apache
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100
Content-Length: 0
Content-Type: text/html
200 OK
Joomla headers displayed by developer tools:
Pragma: no-cache
Date: Thu, 19 Jan 2017 12:19:24 GMT
Last-Modified: Thu, 19 Jan 2017 12:19:25 GMT
Server: Apache
Authorization: : Bearer 123456789
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100
Content-Length: 76888
Expires: Wed, 17 Aug 2005 00:00:00 GMT
Remove double dot from setheader call :
$app = JFactory::getApplication();
$app->setHeader('Authorization', 'Bearer 123456789');
$app->sendHeaders();
Thanks for the suggestion Yoleth. I tested this and got the same result.
However I have found the problem. The Joomla site setting the header is using a component called Site Lock. This is similar to putting the site off line but has some nice features for developers.
Basically Site Lock was preventing the page being served and just returning the headers from the lock page (as it should). I don't know why I didn't see it earlier. Sometimes just can't see the forest for the trees!

How does HTTP max-age works and how to expire cache after some time?

I manage the HTTP caching in my applications. And it's not working as I think it should. Let's get to an actual example:
With the first serve of my PHP page I serve the following HTTP headers:
HTTP/1.1 200 OK
Date: Mon, 12 Dec 2016 16:39:33 GMT
Server: Apache/2.4.9 (Win64) PHP/5.5.12
Expires: Tue, 01 Jan 1980 19:53:00 GMT
Cache-Control: private, max-age=60, pre-check=60
Last-Modified: Mon, 12 Dec 2016 15:57:25 GMT
Etag: "a2883c859ce5c8153d65a4e904c40a79"
Content-Language: en
Content-Length: 326
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
My application manage the validation of Etags and send 304 if nothing has changed and when you refresh the page in the browser (F5) you get (if nothing has changed server side):
HTTP/1.1 304 Not Modified
Date: Mon, 12 Dec 2016 16:43:10 GMT
Server: Apache/2.4.9 (Win64) PHP/5.5.12
Connection: Keep-Alive
Keep-Alive: timeout=5, max=100
Since I serve Cache-Control: private with max-age=60 I would expect that after one minute the cache will be considered obsolete by the browser and it will request a fresh copy (equivalent of a Ctrl+F5 reload) but instead the cache is still valid several days after it's max-age.
Do I misunderstood these HTTP mechanism? Do I send something wrong or maybe miss something?
If a cached response is within the max-age, then it is considered fresh.
If it exceeds the max-age, then it is considered stale.
If a browser needs a resource and it has a fresh copy in the cache, then it will use that without checking back with the server.
If the browser has a stale copy then it will validate that against the server (in this case, using Etags) to see if it needs a new copy of it the cached copy is still OK.

php sends charset header but there is no header command

My browser shows page with incorrect encoding. I have figured out that the server sends headers
HTTP/1.1 200 OK
Date: Thu, 02 Jan 2014 18:21:11 GMT
Server: Apache/2.2.4 (Win32) mod_ssl/2.2.4 OpenSSL/0.9.8k PHP/5.2.12
X-Powered-By: PHP/5.2.12
Content-Length: 4
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=windows-1251
but php script doesn't contain encoding set command. I even added exit on the script top and browser still getting Content-Type: text/html; charset=windows-1251. How so?
First look at default_charset in php.ini. Leave it empty if you do not want a Content-Type header.
In PHP one always can do a header('Content-Type', 'text/html; charset=UTF-8');.
Apache also has a config for Content-Type, but the error probably lies at the PHP side.

Gzip data to be sent to browser in php

I want to Gzip data(css,js) being sent to browser in php . I tried gzcompress, gzencode but both made the file unrecognizable by the browser
Data from firebug for home.css
Date Sat, 10 Sep 2011 22:31:59 GMT
Server Apache/2.2.14 (Ubuntu)
X-Powered-By PHP/5.3.2-1ubuntu4.9
Expires Sat, 17 Sep 2011 22:31:59 GMT
Cache-Control public
Pragma no-cache
Etag e35b61f80bbf8e0dd722c50c65ec6da5
Vary Accept-Encoding
Content-Encoding gzip
Content-Length 25163
Keep-Alive timeout=15, max=92
Connection Keep-Alive
Content-Type text/css
EDIT: I even tried below that too didn't work
<?php
if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'))
ob_start("ob_gzhandler");
else
ob_start();
?>
You need to set the appropriate headers in order for the browser to recognize the file as being compressed.
This really however is a better task for your web server to handle, rather than your code.
try this
<?php
ob_start("ob_gzhandler");
echo file_get_contents("stylesheet.css");
ob_end_flush();
?>
http://php.net/manual/en/function.ob-gzhandler.php
http://www.php.net/manual/en/function.ob-end-flush.php
just tested this with apache, if this isnt working for you you messed something up in your apache config.
headers:
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:180
Content-Type:text/html
Date:Sat, 10 Sep 2011 23:11:18 GMT
Keep-Alive:timeout=5, max=99
Server:Apache/2.2.17 (Win32) mod_ssl/2.2.17 OpenSSL/0.9.8o PHP/5.3.4 mod_perl/2.0.4 Perl/v5.10.1
Vary:Accept-Encoding
X-Powered-By:PHP/5.3.5

Categories