Cache PHP page that echoes CSS code - php

Following the accepted answer here on SO, I am trying to create a stylesheet that is editable with PHP.
What's happening...
I am trying to make the stylesheet (named css.php) cache in the user's browser so that he/she does not have to load it on every pageload, and have set the following headers to do so:
header('Content-Type: text/css;;charset=UTF-8');
header('cache-control: max-age=86400;');
header('Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT;');
header('Expires: Sat, 22 Nov 2014 16:00:00 GMT;');
header('cache-control: max-age=86400;');
These are the other headers sent by default:
Connection:"Keep-Alive"
Content-Encoding:"gzip"
Content-Length:"393"
Date:"Fri, 21 Nov 2014 17:00:50 GMT"
Keep-Alive:"timeout=5, max=99"
Server:"Apache"
Vary:"Accept-Encoding"
X-Frame-Options:"SAMEORIGIN"
However, upon loading a page that references the css.php page multiple times, it continues to reload the CSS page every time.
How do I know this...
I am receiving a hit to the css.php page every time I load the page which uses the stylesheet on my apache server access logs.
I can see that my Firefox browser is accessing the css.php page in the Inspect Element tool. It is receiving a HTTP 200 every time.
What should I do?

Instead of creating a dynamic CSS file, change the standard one each time with PHP's file_put_contents() function.
Example:
file_put_contents("styles.css", $css_input);
That way, browsers will cache the file like normal.
Example:
<link rel="stylesheet" type="text/css" href="styles.css"/>
When you make changes the the actual CSS, browsers will automatically load the new one upon restart.

Related

Setting up cache to consider file-modification date/time

I'm having problems with serving CSS files from PHP. For test I'm just loading content from existing CSS file into PHP variable and than echo it. I want to set headers to allow caching of file until it was modified.
PHP code
$css_file_path = "path-to-existing-css-file";
$file_content = file_get_contents ($css_file_path);
$fmtime = date ("r", filemtime ($css_file_path));
header ("Content-type: text/css");
header ("X-Content-Type-Options: nosniff");
header ("Last-Modified: " . $fmtime);
die ($file_contents);
This is done by simple PHP code as shown above. For some reason it's never cached (tested in latest Firefox only).
I have tried to put this line before die() function to test it.
echo date ("r", time());
And it gets updated all the time. I'm such a caching noob, I admit it, so all I want to do is to make file being cached until new modification arrives.
So far, I have read tones of different posts here and web-wide and mostly found nothing or very poor information on this subject.
What am I missing and is it possible to achieve at all?
To start with
I want to do is to make file being cached until new modification arrives
The only way a browser can know there is a new modification, is by asking the server whether their cached version is still valid.
This is done as followed:
1. Browser requests /style.css
GET /style.css
2. Server sends to browser
HTTP/1.1 200 OK
Last-Modified: Wed 2 Aug 2017 21:28:00 GMT
Cache-Control: must-revalidate, max-age=31536000
... file-contents ...
// 31536000 is about 1 year
3. Next time browser wants that file it sends
GET /style.css
If-Modified-Since: Wed 2 Aug 2017 21:28:00 GMT
4a. Your server can read that header, and verify if the file isn't modified after
the given date. If it isn't, you can reply with a single:
HTTP/1.1 304 Not Modified
... without sending the contents again
4b. If your file was hower modified after Aug 2, you should sent a response simalar
as in step 2
So in code, step 2, add the Cache-Control-header:
header('Cache-Control: must-revalidate, max-age=31536000');
And step 4a, act to the If-Modified-Since request-header:
$css_file_path = "path-to-existing-css-file";
$fmtimestamp = filemtime ($css_file_path);
// Check header set by browser
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $fmtimestamp <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
die(); // We're done here
}
// Otherwise continue as ussualy
$file_content = file_get_contents ($css_file_path);
Alternative solution, without using the If-Modified-Since, but it depends on the situation if this is usable for you:
// Somewhere in your HTML
<link rel="stylesheet" href="/style.css?version=<?php echo filemtime($pathToStyle.css) ?>" />
When your file changes, the link changes and the browser would see it as a new file. In that case you can leave the must-revalidate-part out of the Cache-Control-header and the browser won't reload the style.css unless the max-age expires or cache is cleaned up.

Forcing cache update in browser

I'm using Minify (https://code.google.com/p/minify/) to compress and combine ~30 css files and ~10 javascript files. I've created a group configuration for those files, which is basically an array with the filenames.
Works like a charm, except when one of the scripts is modified: browser cache is not timely update. I used to get a last modified timestamp for each file (using filemtime) to overrule browser caching:
$time = '?' . filemtime( $filename );
$output = '<link type="text/css" rel="stylesheet" href="'.$file_path.'?'.$time.'" />';
I could loop through all 40 files to get the latest timestamp, but that is a very ugly solution. Another way would be e.g. to have a crobjob check it and write the timestamp to a file, which I can then include.
Any other ways before I'm inventing the wheel again?
can you not just add:
src="path/to/file.css?v=<?php date(dmy); ?>"
Just get the modification time of the $file_path...? Personally, I have a much more reasonable number of files (two or three) and each one is individually cached with its mtime. Works beautifully.
I can suggest you a way that you need to define a rewrite rule and implement a handler for static file load. Let say it is assethandler.php, and your rewrite rule;
RewriteRule ^nocache/(.*?)$ assethandler.php?f=$1 [QSA,L]
And in php you can use Cache-Control and Expires for getting always latest file;
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 01 Jul 1990 05:00:00 GMT"); // past date
readfile('path_to_static_files/' . $_GET['f']);
and your static file requests will be like;
<script src="nocache/your.js"/>
Simply, when you make a request to nocache/filename this will be handled as assethandler.php?f=filename and in this handler, cache always disabled and file content served as latest content

How to update browser cache from PHP?

I have a PHP file get_css.php which generates CSS code more than 60 KB long. This code does not change very often. I want this code to be cached in user's browser.
Now, when i visit a HTML page several times which includes get_css.php url to fetch css, my browser is loading all CSS contents from the server each time i visit the page.
Browsers should get the contents from server only if the CSS code is changed on server side. If the css code is not changed, browser will use the css code from the browser cache.
I cannot use any PHP function which is not allowed in Server Safe Mode.
Is it possible? How can i achieve this?
You cannot force a client to revalidate its cache so easily.
Setting a variable query string to its resource won't play well with proxies, but seems to suffice with browsers. Browsers do tend to only redownload the css file if there's a query string change.
<link rel="stylesheet" type="text/css" href="/get_css.php?v=1.2.3">
Potentially, you could play with the naming of the CSS, such as add numbers, but this isn't a great alternative.
You cannot control browser behaviour from PHP, but you can use HTTP codes to tell the browser something.
If the CSS is not changed, just reply with a 304 Not Modified response code:
if ($css_has_not_changed && $browser_has_a_copy) {
http_response_code(304);
} else {
// regenerate CSS
}
This way, the browser will ask for the document (which you cannot control), but you tell him to use the cached copy.
Of course this needs testing, as I have now idea how it will work 'the first time' a browser requests the file (perhaps the request headers can tell you more). A quick firebug test reveals that Firefox requests Cache-Control: no-cache when it is requesting a fresh copy, and Cache-Control: max-age=0 when it has cache.
add normal GET parameter when you including get_css.php like so
<link rel="stylesheet" type="text/css" href="get_css.php?v=1">
Browser will think that it is new link and will load it again.
and in get_css.php use this to make browser cache data
<?php
header("Content-type: text/css");
header('Cache-Control: public');
header('Expires: ' . gmdate('D, d M Y H:i:s', strtotime('+1 year')) . ' GMT');
ob_start("ob_gzhandler");
//echo css here
The browser wants to cache your document by default, but you have to give it enough info to make that possible. One fairly easy way is to send the Last-Modified header, containing the date/time at which your script was last changed. You'll also need to handle the browser's "revalidation" request correctly by checking the incoming Last-Modified date, comparing it to the actual modified date of your script, and returning a 304 Not Modified response (with an empty response body), if the file is unchanged.
It's also a good idea to be sure that your server isn't "magically" sending any other "no-cache" directives. The easiest way to do this is to send a Cache-Control directive that tells the browser exactly what behavior you expect.
Here is a quick explanation of each Cache-Control option.
Something like the following should do the trick:
<?php
// this must be at the top of your file, no content can be output before it
$modified = filemtime(__FILE__);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
$if_modified_since=strtotime($_SERVER["HTTP_IF_MODIFIED_SINCE"]);
if( $modified > $if_modified_since ) {
header('HTTP/1.0 304 Not Modified');
exit();
}
}
header('Cache-Control: must-revalidate');
header('Last-Modified: '.date("r",$modified));
// ... and the rest of your file goes here...
The above example was based heavily on the example, and writeup found here.

how do I control caching for releases with PHP, headers, etc for a backbone project?

So we are doing weekly releases of our project and we are running into problems with clients having old stale versions of certain files.
The stack is backbone with requirejs with backbone boilerplate and an Apache2 server with PHP backend.
We have the index html file that gets loaded, template HTML files that get loaded using AJAX, and then all the js files.
This question seems related to https://stackoverflow.com/questions/12103341/backbone-boilerplate-disable-caching but I didn't see a good answer there.
I've heard that cache-control headers and mod expires and mod headers for PHP might be helpful, but I don't know how to put it all together.
Essentially, what we want to do is make sure on a release of new code to the prod server that everything is not cached at least once. THen after that, normal caching to increase load speed would be ideal.
At the very least, I would liek to understand how to completely prevent these things from being cached.
Any ideas?
I've found it difficult to control the user client's browser as far as caching is concerned. One trick I've used in the past is to append a random number to my JS file URL. Like
<script src="https://www.mydomain.com/myjsfile.js?123456789"></script>
OR
<script src="https://www.mydomain.com/myjsfile.js?releaseID=123456789"></script>
That causes the client to treat it as a new file altogether. You could assign a random number to each release and that should cause your users' browsers to pull the new JS file again. Same applies to CSS.
I hope this helps. Good luck.
With requirejs, there is a parameter called urlargs it adds a parameter to all your requirejs requests and can be employed to force cache update. The example on the page use a timestamp, but you probably looking for a build version.
However, you should use r.js to build a production version of your scripts (it compiles and minifies all files loaded with require and produces a single library). This will reduce load time and save you a lot of worries. You would them link just this one library and have the build version in the file name itself (something like backbone.app.1.0.23456.js).
The caching of files on the client side is defined by two values: a meta-information in the <head>-section of the called html file and an HTTP-Header.
In the HTML file you may give the two meta-tags cache-control and expires to define a weekly expiration date by giving a simple php-command:
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="PRIVATE">
<?php
$daysDelta = 7-date("w");
$expiration = date(DATE_RFC1123, mktime(0, 0, 0, date("m"),
date("d")+$daysDelta+1, date("Y")));
?>
<META HTTP-EQUIV="EXPIRES" CONTENT="<?= $expiration ?>">
This would kill the cache on Monday morning. Another method would be the cache setting via HTTP-Header:
<?php
header("Cache-Control: private");
header("Expires: <?= $expiration ?>");
?>
Using both methods, you will have caching during the week, but after weekend, the clients will invalidate their caches and repull the data.
To remove caching from a page you need to include this at the top of your page:-
$this->output->set_header("Expires: Tue, 01 Jan 2000 00:00:00 GMT");
$this->output->set_header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
$this->output->set_header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
$this->output->set_header("Cache-Control: post-check=0, pre-check=0", false);
$this->output->set_header("Pragma: no-cache");
Appending random number to the uri breaks client caching.
Instead, append the file's last modification time.
<script src="myjsfile.<?=filemtime('myjsfile.js')?>.js"></script>
Handling that would require and .htaccess rule, such as
RewriteRule ([^/]*)\.[0-9]*\.css$ $1.css [L,NC]
And additionally, provide .htaccess with long term caching instructions
<IfModule mod_expires.c>
ExpiresActive on
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
</IfModule>

Dynamically disable caching of Javascript/CSS with PHP?

I've coded an option called 'devmode' in my web app, which basically means 'no caching'. The app normally outputs an automatically minified (and aggregated) version of the Javascript and CSS, but the devmode option overrides this.
However, we still have the browser cache. So, without further ado, how can I disable caching of ALL components on a page if a certain PHP boolean is true?
Cheers
Edit: might interest you to know that I'm running Apache, and one idea I had was to force .js and .css to be parsed as PHP (which is straightforward), and somehow 'inject' a little piece of PHP code at the start of each.
A "quick and dirty" approach for debugging/developing, you could call all components in your HTML with a random (or time-based) query-string. For instance:
<img src="logo.png?uniqecall=20111026035500" />
, which would look like this in your PHP code:
print '<img src="logo.png?uniqecall=' . date("YmdHis") . '" />';
etc...
.htaccess
RewriteRule ^no-cache/(.*?)$ no-cache.php?file=$1 [QSA,L]
no-cache.php
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
readfile('static/'.$_GET['file']);
Assuming you won't hack yourself :)

Categories