I am using gzip on my css to compress on page load. To do this I have made my css (stylesheet.css.php) file.
This is my PHP script included inside file :
<?php
ob_start ("ob_gzhandler");
header ("content-type: text/css; charset: UTF-8");
header ("cache-control: must-revalidate");
$offset = 60 * 60;
$expire = "expires: " . gmdate ("D, d M Y H:i:s", time() + $offset) . " GMT";
header ($expire);
?>
/*css stuff*/
I understand that the ($offset = 60 * 60;) is "sending an 'Expires' header, to set an age on how long our cached file will last. Here we set it to expire in one hour."
My problem is obviously if it expires every hour and I change a CSS setting it wont update until an hour, thus giving a user un-updated stylesheet? How do I change it to say update every one min so it fetches an updates stlyesheet every minute not hour? Would I just put ($offset = 60;) meaning 1 second?
My other problem is in internet explorer when I use HTTPS and say user hits back button IE gives message "do you want to view only the webpage content that was delivered securely" and you can click 'Yes' or 'No'. If you click 'No' then the CSS does not get loaded on that previous page. I am accessing my CSS using link href='/css/stylesheet.css' path format so cant see why its not secure?
Thanks
To shorten the time the clients wait before requesting the file again, you can shorten your 'offset' like you said. The unit the time() function uses is seconds, so '60' would mean one minute. If you update your css regularly, maybe you should not use the expires header at all to save you the hassle, since most clients automatically cache css files.
There are two solutions for the https problem. Either you check if the request comes in via http oder https and write the url for your css file accordingly, or you use just //folder/path/to/cssfile.php as the path to your css file. Note the 2 slashes and the absence of 'http'. This notation automatically gives you the right protocol, like here.
You can't do that.
Expire is meant to don't make an additional request by the client.
You will have to wait 1hour to be sure that all user cache are flushed.
The dirty way is to append a param whenever you change your css.
Something like:
style.css?param=123
What you are suggesting is a bad idea. The way you want to set it up will totally break all caching of your CSS file and have you regenerating it on the fly for every request.
It is definitely a best practice to serve gzipped files, the problems are the way you're doing it. First, hitting the PHP interpreter and gzipping on the fly on every request is very bad for performance and more than negates the benefit of serving a compressed file. Plain HTTP file requests are going to be answered 10x faster than something than hits PHP.
The other issue is that, yes, you can reduce the expires time to 60 seconds (effectively, to zero ... but as svanelten says, at that point you might as well not send the header at all). There's no problem with not sending an expires header (or a short one) if that's really what you want, but doing this in combination with dynamically generating the page is bad if the content isn't actually changing that often. This breaks If-Modified-Since, which browsers send to make conditional requests so that they don't have to redownload the file if it hasn't changed. You are not setting a Last-Modified time so your last-modified time is effectively always "right now."
There are three ways to fix these problems (and accomplish your original goal of serving a compressed CSS file faster, in a cache-friendly way):
Just enable gzip encoding in your web server's config and serve the CSS as a regular file. Then the web server handles everything. i.e. in Apache2, turn on mod_deflate.
The next easiest way is to gzip the file and let your web server serve it. That way, the web server takes care of sending Last-Modified (which will just be the actual file modification time). And, it will send HTTP/304 Not Modified in response to conditional requests, so browsers don't have to re-download the file.
For instance, if you are using Apache, you could just gzip the file and put in the same directory as your regular css file (so you have, for example, file.css and file.css.gz). Then put this into your Apache config or .htaccess:
<Files *.css.gz>
AddType "text/css" .gz
AddEncoding gzip .gz
</Files>
RewriteEngine On
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)$ $1.gz [QSA,L]
The last way (if you must use PHP for this) is to have PHP cache the gzipped file, send the Last-Modified header for when the file was actually last modified, and respond to If-Modified-Since and If-None-Match requests by actually checking these things and then returning a 304 or 200 response accordingly. I do not recommend this because it still hits the PHP interpreter, and you are basically re-implementing webserver functions in PHP, but I've done it in special cases. It's the worst solution but at least better than dynamically generating everything.
Related
I have a script called "image.php" that is used to count impressions and then print the image.
This script is called in this way:
<img src="path/image.php?id=12345" />
And it's used very often by my users, i see thousand of request per day
So I am looking to understand what is the best way to output the image at the end of this script:
Method 1 (actually in use):
header("Content-type: $mime"); //$mime is found with getimagesize function
readfile("$image_url");
exit;
Method 2 (pretty sure that is slowest):
header("Content-type: $mime");
echo file_get_contents("$image_url");
exit;
Method 3:
header('Location: '.$image_url);
exit();
Is method 3 better / faster than method 1?
Ok first of all Method 3 is way faster when redirected to the original file.
The first 2 methods need file access and read the file and also they don't use the browser cache!
Also when you store the rendered images, you can better let apache handle your static files.
Apache is way faster than PHP and it uses the right browser caching (3 or 4 times faster wouldn't be a suprise).
What happens is when you request a static file, apache send the Last-Modified header
If your client requests the same image again it sends the If-Modified-Since header with that same date. If the file isn't changed you server respond with an 304 Not Modified header without any data wich saves you a lot IO operations (Besides the ETAG header wich is also used)
For your impressions count of the image, you could create a cronjob that parses your apache access logs so the end-user won't even notice it. But in your case it's easier to count the impressions in your script and then redirect
Essentially, what readfile does is it reads the file directly into the output buffer while file_get_contents loads the file into the memory (string). So, when you output the results the data is copied from the memory into the output buffer, making it two times slower than readfile.
I want to force download a pdf,doc or docx file.
With the following code,Pdf files get opened in my tab instead of getting downloaded.
I have a table having download link in every row.I want to download file on click of this link.
foreach($a as $id = > $item) {
echo '<tr><td><a href="http://staging.experiencecommerce.com/ecsite-v3/uploads/'.substr($item['f_resume'], 63).'" ">';
//Note:substr($item['f_resume'], 63) is file_name
echo '</a></td><td>'.$item['f_date'].'</td></tr>';
}
I went through some Question on SO with same problem and tried their solution,but in Vain.
When I included the solution inside foreach,the page downloads file on load and when I place the solution outside ,the Php script gets downloaded.
Where am I going wrong?
You can set headers that will force downloading:
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="filenamehere.pdf"');
If you're not using PHP to provide content of that files you can set headers using eg. .htaccess (requires mod_headers).
<FilesMatch ".pdf$">
FileETag None
<ifModule mod_headers.c>
Header set Content-Type "application/force-download"
</ifModule>
</FilesMatch>
After our whole chat session I think we can leave this answer here, just for future reference:
As seen in your initial post, once you click the link, you relinquish all control to the browser so it will treat the file as it sees fit. Usually this involves trying to find whatever application or plugin the system can find to treat your file.
Whenever you want to force the download of the file all you have to do is divorce the presentation itself from the task at hand. In this particular case:
1 - Create a new script that will identify the file via parameters passed and force the download on it, as seen on the examples at this site php.net/manual/en/function.readfile.php.
2 - Rework the presentation so the links do no longer point to the file itself, but to the new script with the appropriate parameters (like, for example, download_file.php?file_id=#FILE_ID#).
3 - Treat the case in which the file can not be found by, for example, die("The file could not be found") before setting the headers.
One word of advice: do not use the file location as a parameter!!!. Use instead something that you can retrieve from a database to then collect the file location. If you pass the file location itself as a parameter nothing is stopping me from doing this:
http://yoursite.com/download_file.php?file=download_file.php
http://yoursite.com/download_file.php?file=index.php
http://yoursite.com/download_file.php?file=whatever_file_there_is
With the adequate circumstances, like autodetection of the xtype for the requested file, it would allow me to access your code and exploit any possible flaws
One second and final note of advice: php can only output one thing at once. If you want it to output a website you can't output a pdf file afterwards. That's why - among other reasons - you divorce the different tasks at hand and also, that's why everything went awry when you tried directly including the download script after each link was printed.
If it helps, imagine php not as your usual real-time programming language, but as a printer. It will print everything you tell it to and serve it in reasonably sized chunks. There's no stopping it until the end is reached, there's no possible exploring two opposite branching code paths unless you call the script again with the appropriate conditions.
Hope the chat helped you.
Here's the thing.
I'm serving somewhat compressed CSS content
(`str_replace(array("\r", "\n", "\t", '\s\s+'), '', cssGoesHere)`)
via a PHP file in my page:
<link rel="stylesheet" type="text/css" href="/css/loader.css.php" />
The question is: how do I make the browser cache the css returned, BUT update the cache if the content is changed?
The PHP file is not being modified, so appending something like
<?php echo filemtime('/css/loader.css.php'); ?>
to the href attribute is not an option. Can this be solved with headers, and if so, how? Because AFAIK if I serve it like I wrote above, the browser will just cache the result and keep reusing the cache (provided, of course, the browser is enabled/capable of doing so), but I need it to know when the content is changed.
Edit: I've made a github project with my code (though I did change it alot for more flexibility since I wrote this). Here's the link: https://github.com/jurchiks/YACC
If you have any suggestions, write them to my e-mail or smth.
It can be solved by headers cache-control, BUT this have proved to be not very effective. Because some browser do overwrite or modify your forced settings.
I can't see where you getting the CSS data from, if it's a CSS file you could get the filemtime of the css file as version indicator or simply supply somewhere a version string which has to be changed each time the CSS file changes.
By the way, str_replace(array("\r", "\n", "\t", '\s\s+'), does not effectively remove all newlines. Your should use something like
$foo = nl2br($string, FALSE);
$bar = str_replace(array('<br>', '\s\s+'), '', cssGoesHere)
instead.
HTH :)
The only way to reliably make the file load upon change is the method you state. Otherwise, the page will cache the file and keep it for as long as it thinks necessary. It can't check to see whether the page has changed or not without requesting and downloading at least the headers, and if it's gone that far it might as well just download the rest of the page, as the bandwidth will be minimal.
The best alternative if you really can't append the modified date to the HTML, would be to set the cache headers via PHP.
header("Cache-Control: max-age=time-in-seconds");
This doesn't always work though, as server settings and browser settings can override this, especially in aggressive caching browsers such as Internet Explorer.
You could add a version number to the CSS link href and use mod rewrite to route it back to the original CSS file.
For example. Let's say you start with version 1 (note the version appendix to the file name):
<link rel="stylesheet" type="text/css" href="/css/loader-v001.css.php" />
The browser will cache this file. The next time you update the CSS file and you don't want the browser to use the cache file, simply change the version number like this:
<link rel="stylesheet" type="text/css" href="/css/loader-v002.css.php" />
Then, in your mod rewrite, route any request for the file loader-v{x}.css.php to loader.css.php.
I am not sure if this is 'automated' enough for your needs, as it does require manual changing of the version number. However, the different file name will ensure the browser sees it as an actual 'new' file and re-downloads it.
use this in your css php page:
header('Content-type: text/css');
header("Cache-Control: no-cache, must-revalidate");
you can see more information about headers in PHP's official documentation
i would suggest to keep somewhere the time that the cache content was generated for example
style.php which holds the CSS data was last modified at 21/06/2012
and cache content was last generated at 20/06/2012 running a comparison on dates in strtotime() integers, you see that the cached content must be generated again from the new content.
My situation
I am currently using Cache Busting when I include CSS files like this:
echo "<link href='stylesheet.css?" . filemtime('stylesheet.css') . "' />"
My goal
Now I would like to do something similar for the images I am including inside my CSS file.
The problem
The problem is that I cannot use PHP inside my CSS files and I would rather keep my CSS files seperated.
My question
How can I add the filemtime() to the images inside my CSS files while keeping the files seperated?
Edit
I would like to use Far Future Expires headers to cache the files.
You could actually rename your css files as style.css.php and then use PHP inside them. As long as the post-processing result is in the proper CSS format, it should work. I've done it in the past. I'm not sure if it's necessary, but if that gives you problems, you can use a header('Content-type...') kind of thing and ensure it's sent as a CSS file.
To achieve cache-busting, the best way is to send the proper headers. Make sure Apache is configured to send a Expires: now header. So in a .htaccss file:
Header always set Cache-Control "no-store, no-cache, must-revalidate"
Header always set Expires "Thu, 01 Jan 1970 00:00:00 GMT"
That will always force no-caching of all content in its directory and any under.
However, if you wanted to conditionally cache, what I would suggest, is doing one of a few things.
Include a version number in the name of the CSS file. So you'd have a file that looks like mycss.1.css, mycss.2.css. This takes a little more work since you need to coordinate both filenames. But it's better since you aren't sending the files with PHP (no resource hit), you can use a CDN (even better) and you can still take advantage of far-future expires headers.
Set the Cache-Control: must-revalidate header and a proper E-Tag header so that all it needs to do is send a 304 Not Modified header if the content didn't change...
I have a php dynamically generated image which I need to write to file to call later. My problem is that I need this image to have appropriate expiration headers included in it. There are a massive number of these and their headers vary individually file-by-file making .htaccess controls not an option.
I can write expiration headers if I'm outputting the image directly to the browser with this:
header("Content-Type: image/jpeg");
header('Expires: "' . gmdate("D, d M Y H:i:s", $expirationDate) . '"');
imagepng($image, NULL);
Or I can write the image to a file to be used later with this:
imagepng($image, $filepath)
But I can't for the life of me figure out how to combine those two and write the image to a file while including its expiration headers. How would you go about writing an image file with an expires header?
I think your best bet it to server the file just as you are, something like:
header("Content-Type: image/jpeg");
header('Expires: "' . gmdate("D, d M Y H:i:s",
$expirationDate) . '"');
imagepng($image, NULL);
Sure you're using php to serve a static file, but the expire header is going to limit repeat requests.
Update: Since $image is a generated file, on the first request generate and save the image, then output it. On additional requests, just output the already generated image. Essentially the expire headers are controlling the browser's cache, while you need to implement some kind of caching on the server to avoid generating the same output multiple times.
So you're looking at two different kinds of caching. You can do them in the same script, with a combination of two scripts - really however you want.
Unless you can set a standard expire header with apache (which you say you can't, since it varies), I believe this is your best (if not only) choice.
Of course there is the convoluted and complex way:
Set up mod_rewrite to send requests for missing images to your php script.
Append some session id to the image request (so it's unique to the browser).
Have the php script send the expire header, and the image content.
Have the php script link the real static image to the session specific image name.
Or something like that. I'd just serve them all up using php.
Update: Or use mod_asis from VolkerK's great answer.
If you really want to store both the headers and the content in files on the server you could use mod_asis:
In the server configuration file, associate files with the send-as-is handler e.g.
AddHandler send-as-is asis
The contents of any file with a .asis extension will then be sent by Apache to the client with almost no changes. In particular, HTTP headers are derived from the file itself according to mod_cgi rules, so an asis file must include valid headers, and may also use the CGI Status: header to determine the HTTP response code.
Your php script then would write both the headers and the content to files that are handled as send-as-is by the apache webserver.
Perhaps all you have to do is exactly ...nothing, except writing the image data to the disc.
Depending on the webserver you're using some caching mechanisms work out of the box for static files (which you would create with the php script).
If you're using apache's httpd take a look at http://httpd.apache.org/docs/2.2/mod/core.html#fileetag and http://httpd.apache.org/docs/2.2/caching.html. By default httpd will also send a last-modified header and it supports If-Modified-Since request headers.
When your php script changes the image files the ETag changes as well and/or the If-Modified-Since condition would be met and the httpd sends the data. Otherwise it would only send a response saying "nothing has changed" to the client.