HTML5 - ApplicationCache not updating (only) some files - javascript

I've got an application that is using AppCache to store resources in the browser's cache. I've been able to set up the manifest on a PHP file in order to write automatically all files that must be cached (properly filtered). The headers of this file are the following:
header('Content-Type: text/cache-manifest');
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
It's called appcache.php and it's referenced correctly in my HTML:
<html manifest="appcache.php">
Everytime I update the manifest, the app detects the update and the events are triggered properly, updating the cache and refreshing the page so they are reloaded.
However, some of the files (mainly Javascript and CSS files) do not refresh properly. Sometimes it takes a few manifest updates (with the whole process), sometimes it takes just one, sometimes it takes forever and I have to delete the browser cache or go Incognito mode.
The app detects the update, so the manifest is not getting catched and it's getting updated correctly. The problem is that, even the files are downloaded again, they are still served in an older version. And the main problem is that this only happens in some files (sometimes they're some css files, sometimes they're js, and so on: the files not updated are random).
Actually, I'm using Chrome v59, and it happens the same on my Android device with Chrome v60.
Is there any explanation to why they're not refreshing correctly? Is this a known bug? Is there anything I can do to solve it?
UPDATE
It seems that it happens the most when I've done some updates with short time between them. After waiting 24h or so, it works at the first try. Maybe this can help you to guess why it happens.
Thank you.

It is possible that your manifest file is getting cached, you can set the expires headers for it to expire rightaway
I would suggest adding swapCache method
$(function() {
if (window.applicationCache) {
applicationCache.addEventListener('updateready', function() {
if (window.applicationCache.status ==
window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
console.log("appcache updated");
window.location.reload();
}
});
}
}
Other thing ,To force any new (or changed) file to be downloaded, you must
update the manifest file (Which seems like you are doing it).
The most common one is that you might not be serving the manifest with the right mime type (text/cache-manifest).
You might be getting error, The easiest way to check error is to open the page in Chrome and look in the console and the resources tab under AppCache to see if there is an error (it will complain about the file being served up incorrectly. You can also test it with the curl -I command:
curl -I $manifest_file_URL

Related

How to hard reload/refresh a page using javascript

How do we do a hard reload/refresh in Chrome using Javascript?
window.location.reload(true);
location.reload(true);
didn't do it for me.
===========================
What I meant by 'didn't do it for me...'
The session cookies (ex. JSESSIONID) were not renewed specially the HttpOnly ones.
What I want to achieve...'
I wanted to reload the page like it's the first time I accessed the URL.
I wanted to simulate the steps below (which is like accessing the URL for the first time)
- Open browser
- Type URL
- Hit Enter
I wonder if there is a more powerful Javascript command that reloads as if it is the first time.
The best (and practically only) way to ensure a page is hard reloaded is by using your server. One of the ways you can do this is by serving headers, when a page is requested, that invalidate resources such as Cache-Control which will tell the browser to not cache resources and always revalidate resources which means everything must be redownloaded each time.
I would not recommend serving this header on every request in your production application, though.
Cache-Control: no-store, max-age=0
On your page you can add meta tags that will tell the browser what to do, provided there weren't headers already passed to the browser. This depends on how you serve your content, but you can add the following to your head element which will tell the browser to store absolutely nothing and is equivalent to hitting a page for the first time:
<head>
...
<meta http-equiv="Cache-Control" content="no-store, max-age=0">
...
</head>
location.reload() should work. Works for me!

How to circumvent browser caching? [duplicate]

Is there a way I can put some code on my page so when someone visits a site, it clears the browser cache, so they can view the changes?
Languages used: ASP.NET, VB.NET, and of course HTML, CSS, and jQuery.
If this is about .css and .js changes, then one way is "cache busting" by appending something like "_versionNo" to the file name for each release. For example:
script_1.0.css // This is the URL for release 1.0
script_1.1.css // This is the URL for release 1.1
script_1.2.css // etc.
or after the file name:
script.css?v=1.0 // This is the URL for release 1.0
script.css?v=1.1 // This is the URL for release 1.1
script.css?v=1.2 // etc.
You can check this link to see how it could work.
Look into the cache-control and the expires META Tag.
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
<META HTTP-EQUIV="EXPIRES" CONTENT="Mon, 22 Jul 2002 11:12:01 GMT">
Another common practices is to append constantly-changing strings to the end of the requested files. For instance:
<script type="text/javascript" src="main.js?v=12392823"></script>
Update 2012
This is an old question but I think it needs a more up to date answer because now there is a way to have more control of website caching.
In Offline Web Applications (which is really any HTML5 website) applicationCache.swapCache() can be used to update the cached version of your website without the need for manually reloading the page.
This is a code example from the Beginner's Guide to Using the Application Cache on HTML5 Rocks explaining how to update users to the newest version of your site:
// Check if a new cache is available on page load.
window.addEventListener('load', function(e) {
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
// Swap it in and reload the page to get the new hotness.
window.applicationCache.swapCache();
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
} else {
// Manifest didn't changed. Nothing new to server.
}
}, false);
}, false);
See also Using the application cache on Mozilla Developer Network for more info.
Update 2016
Things change quickly on the Web.
This question was asked in 2009 and in 2012 I posted an update about a new way to handle the problem described in the question. Another 4 years passed and now it seems that it is already deprecated. Thanks to cgaldiolo for pointing it out in the comments.
Currently, as of July 2016, the HTML Standard, Section 7.9, Offline Web applications includes a deprecation warning:
This feature is in the process of being removed from the Web platform.
(This is a long process that takes many years.) Using any of the
offline Web application features at this time is highly discouraged.
Use service workers instead.
So does Using the application cache on Mozilla Developer Network that I referenced in 2012:
Deprecated This feature has been removed from the Web standards.
Though some browsers may still support it, it is in the process of
being dropped. Do not use it in old or new projects. Pages or Web apps
using it may break at any time.
See also Bug 1204581 - Add a deprecation notice for AppCache if service worker fetch interception is enabled.
Not as such. One method is to send the appropriate headers when delivering content to force the browser to reload:
Making sure a web page is not cached, across all browsers.
If your search for "cache header" or something similar here on SO, you'll find ASP.NET specific examples.
Another, less clean but sometimes only way if you can't control the headers on server side, is adding a random GET parameter to the resource that is being called:
myimage.gif?random=1923849839
I had similiar problem and this is how I solved it:
In index.html file I've added manifest:
<html manifest="cache.manifest">
In <head> section included script updating the cache:
<script type="text/javascript" src="update_cache.js"></script>
In <body> section I've inserted onload function:
<body onload="checkForUpdate()">
In cache.manifest I've put all files I want to cache. It is important now that it works in my case (Apache) just by updating each time the "version" comment. It is also an option to name files with "?ver=001" or something at the end of name but it's not needed. Changing just # version 1.01 triggers cache update event.
CACHE MANIFEST
# version 1.01
style.css
imgs/logo.png
#all other files
It's important to include 1., 2. and 3. points only in index.html. Otherwise
GET http://foo.bar/resource.ext net::ERR_FAILED
occurs because every "child" file tries to cache the page while the page is already cached.
In update_cache.js file I've put this code:
function checkForUpdate()
{
if (window.applicationCache != undefined && window.applicationCache != null)
{
window.applicationCache.addEventListener('updateready', updateApplication);
}
}
function updateApplication(event)
{
if (window.applicationCache.status != 4) return;
window.applicationCache.removeEventListener('updateready', updateApplication);
window.applicationCache.swapCache();
window.location.reload();
}
Now you just change files and in manifest you have to update version comment. Now visiting index.html page will update the cache.
The parts of solution aren't mine but I've found them through internet and put together so that it works.
For static resources right caching would be to use query parameters with value of each deployment or file version. This will have effect of clearing cache after each deployment.
/Content/css/Site.css?version={FileVersionNumber}
Here is ASP.NET MVC example.
<link href="#Url.Content("~/Content/Css/Reset.css")?version=#this.GetType().Assembly.GetName().Version" rel="stylesheet" type="text/css" />
Don't forget to update assembly version.
I had a case where I would take photos of clients online and would need to update the div if a photo is changed. Browser was still showing the old photo. So I used the hack of calling a random GET variable, which would be unique every time. Here it is if it could help anybody
<img src="/photos/userid_73.jpg?random=<?php echo rand() ?>" ...
EDIT
As pointed out by others, following is much more efficient solution since it will reload images only when they are changed, identifying this change by the file size:
<img src="/photos/userid_73.jpg?modified=<? filemtime("/photos/userid_73.jpg")?>"
A lot of answers are missing the point - most developers are well aware that turning off the cache is inefficient. However, there are many common circumstances where efficiency is unimportant and default cache behavior is badly broken.
These include nested, iterative script testing (the big one!) and broken third party software workarounds. None of the solutions given here are adequate to address such common scenarios. Most web browsers are far too aggressive caching and provide no sensible means to avoid these problems.
Updating the URL to the following works for me:
/custom.js?id=1
By adding a unique number after ?id= and incrementing it for new changes, users do not have to press CTRL + F5 to refresh the cache. Alternatively, you can append hash or string version of the current time or Epoch after ?id=
Something like ?id=1520606295
<meta http-equiv="pragma" content="no-cache" />
Also see https://stackoverflow.com/questions/126772/how-to-force-a-web-browser-not-to-cache-images
Here is the MDSN page on setting caching in ASP.NET.
Response.Cache.SetExpires(DateTime.Now.AddSeconds(60))
Response.Cache.SetCacheability(HttpCacheability.Public)
Response.Cache.SetValidUntilExpires(False)
Response.Cache.VaryByParams("Category") = True
If Response.Cache.VaryByParams("Category") Then
'...
End If
Not sure if that might really help you but that's how caching should work on any browser. When the browser request a file, it should always send a request to the server unless there is a "offline" mode. The server will read some parameters like date modified or etags.
The server will return a 304 error response for NOT MODIFIED and the browser will have to use its cache. If the etag doesn't validate on server side or the modified date is below the current modified date, the server should return the new content with the new modified date or etags or both.
If there is no caching data sent to the browser, I guess the behavior is undetermined, the browser may or may not cache file that don't tell how they are cached. If you set caching parameters in the response it will cache your files correctly and the server then may choose to return a 304 error, or the new content.
This is how it should be done. Using random params or version number in urls is more like a hack than anything.
http://www.checkupdown.com/status/E304.html
http://en.wikipedia.org/wiki/HTTP_ETag
http://www.xpertdeveloper.com/2011/03/last-modified-header-vs-expire-header-vs-etag/
After reading I saw that there is also a expire date. If you have problem, it might be that you have a expire date set up. In other words, when the browser will cache your file, since it has a expiry date, it shouldn't have to request it again before that date. In other words, it will never ask the file to the server and will never receive a 304 not modified. It will simply use the cache until the expiry date is reached or cache is cleared.
So that is my guess, you have some sort of expiry date and you should use last-modified etags or a mix of it all and make sure that there is no expire date.
If people tends to refresh a lot and the file doesn't get changed a lot, then it might be wise to set a big expiry date.
My 2 cents!
I implemented this simple solution that works for me (not yet on production environment):
function verificarNovaVersio() {
var sVersio = localStorage['gcf_versio'+ location.pathname] || 'v00.0.0000';
$.ajax({
url: "./versio.txt"
, dataType: 'text'
, cache: false
, contentType: false
, processData: false
, type: 'post'
}).done(function(sVersioFitxer) {
console.log('Versió App: '+ sVersioFitxer +', Versió Caché: '+ sVersio);
if (sVersio < (sVersioFitxer || 'v00.0.0000')) {
localStorage['gcf_versio'+ location.pathname] = sVersioFitxer;
location.reload(true);
}
});
}
I've a little file located where the html are:
"versio.txt":
v00.5.0014
This function is called in all of my pages, so when loading it checks if the localStorage's version value is lower than the current version and does a
location.reload(true);
...to force reload from server instead from cache.
(obviously, instead of localStorage you can use cookies or other persistent client storage)
I opted for this solution for its simplicity, because only mantaining a single file "versio.txt" will force the full site to reload.
The queryString method is hard to implement and is also cached (if you change from v1.1 to a previous version will load from cache, then it means that the cache is not flushed, keeping all previous versions at cache).
I'm a little newbie and I'd apreciate your professional check & review to ensure my method is a good approach.
Hope it helps.
In addition to setting Cache-control: no-cache, you should also set the Expires header to -1 if you would like the local copy to be refreshed each time (some versions of IE seem to require this).
See HTTP Cache - check with the server, always sending If-Modified-Since
There is one trick that can be used.The trick is to append a parameter/string to the file name in the script tag and change it when you file changes.
<script src="myfile.js?version=1.0.0"></script>
The browser interprets the whole string as the file path even though what comes after the "?" are parameters. So wat happens now is that next time when you update your file just change the number in the script tag on your website (Example <script src="myfile.js?version=1.0.1"></script>) and each users browser will see the file has changed and grab a new copy.
Force browsers to clear cache or reload correct data? I have tried most of the solutions described in stackoverflow, some work, but after a little while, it does cache eventually and display the previous loaded script or file. Is there another way that would clear the cache (css, js, etc) and actually work on all browsers?
I found so far that specific resources can be reloaded individually if you change the date and time on your files on the server. "Clearing cache" is not as easy as it should be. Instead of clearing cache on my browsers, I realized that "touching" the server files cached will actually change the date and time of the source file cached on the server (Tested on Edge, Chrome and Firefox) and most browsers will automatically download the most current fresh copy of whats on your server (code, graphics any multimedia too). I suggest you just copy the most current scripts on the server and "do the touch thing" solution before your program runs, so it will change the date of all your problem files to a most current date and time, then it downloads a fresh copy to your browser:
<?php
touch('/www/sample/file1.css');
touch('/www/sample/file2.js');
?>
then ... the rest of your program...
It took me some time to resolve this issue (as many browsers act differently to different commands, but they all check time of files and compare to your downloaded copy in your browser, if different date and time, will do the refresh), If you can't go the supposed right way, there is always another usable and better solution to it. Best Regards and happy camping. By the way touch(); or alternatives work in many programming languages inclusive in javascript bash sh php and you can include or call them in html.
For webpack users:-
I added time with chunkhash in my webpack config. This solved my problem of invalidating cache on each deployment. Also we need to take care that index.html/ asset.manifest is not cached both in your CDN or browser. Config of chunk name in webpack config will look like this:-
fileName: [chunkhash]-${Date.now()}.js
or If you are using contenthash then
fileName: [contenthash]-${Date.now()}.js
This is the simple solution I used to solve in one of my applications using PHP.
All JS and CSS files are placed in a folder with version name. Example : "1.0.01"
root\1.0.01\JS
root\1.0.01\CSS
Created a Helper and Defined the version Number there
<?php
function system_version()
{
return '1.0.07';
}
And Linked JS and SCC Files like below
<script src="<?= base_url(); ?>/<?= system_version();?>/js/generators.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="<?= base_url(); ?>/<?= system_version(); ?>/css/view-checklist.css" />
Whenever I make changes to any JS or CSS file, I change the System Verson in Helper and rename the folder and deploy it.
I had the same problem, all i did was change the file names which are linked to my index.html file and then went into the index.html file and updated their names, not the best practice but if it works it works. The browser sees them as new files so they get redownloaded on to the users device.
example:
I want to update a css file, its named styles.css, change it to styless.css
Go into index.html and update , and change it to
in case interested I've found my solution to get browsers refreshing .css and .js in the context of .NET MVC (.net fw 4.8) and the use of bundles.
I wanted to make browsers refresh cached files only after a new assembly is deployed.
Buinding on Paulius Zaliaduonis response, my solution is as follows:
store your application base url in the web config app settings (the HttpContext is not yet available at runtime during the RegisterBundle...), then make this parameter changing according to the configuration (debug, staging, release...) by the xml transform
In BundleConfig RegisterBundles get the assembly version by the means of reflection, and...
...change the default tag format of both styles and scripts so that the bundling system generates link and script tags appending a query string parameter on them.
Here is the code
public static void RegisterBundles(BundleCollection bundles)
{
string baseUrl = system.Configuration.ConfigurationManager.AppSettings["by.app.base.url"].ToString();
string assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Styles.DefaultTagFormat = $"<link href='{baseUrl}{{0}}?v={assemblyVersion}' rel='stylesheet'/>";
Scripts.DefaultTagFormat = $"<script src='{baseUrl}{{0}}?v={assemblyVersion}'></script>";
}
You'll get tags like
<script src="https://example.org/myscriptfilepath/script.js?v={myassemblyversion}"></script>
you just need to remember to to build a new version before deploying.
Ciao
Do you want to clear the cache, or just make sure your current (changed?) page is not cached?
If the latter, it should be as simple as
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">

Reload javascript and css files for a website in Safari

This drives me crazy. Every browser and device has it's own way to clear the cache and reload the .js and .css files. For example, the only way I can get do this in Chrome is to go to go to my login page, do an F12 to load the debugger and proceed. This seems to be the only way to do it windows.
On the old safari in windows you could do it from the menu, as you seem to be able to do in Safari on the IMac desktop.
But how do you do in on the IPhone or IPad??????
I am using the IMac to debug the phone, but no matter how I do it, the .js file stays the same. When I look on the website at the page, the changes are there.
I have tried crashing the phone, double tapping the refresh button, putting it in private mode and back. Nothing works. What was interesting was that I tried it in private move and in the Develop menu I could see two versions of the .js file - one with the changes and one without. When I went back to the non-private mode, the old .js files (no changes) were there.
I also tried to delete the website data for my site and that didn't work.
Anyone know how to do it??????
Thanks,
Tom
Append a query string when including your JS or CSS and change it when you update the code. It will invalidate the cached local versions of this file.
<script src="test.js?1234567"></script>
It's common to use a timestamp.
2 answers:
First Answer: Hit the tabs icon, then select Private and paste&go the URL if you only need to force refresh the one time. The Private session will never have a cached version of anything unless it is cached at an intervening proxy. Of course it will cache subsequent requests for the same file. You can close the Private session and create a new one, which is roughly on par with the annoyance level of switching to/from the Settings app.
Second Answer: If you are a developer and you're using Apache, you can set up an .htaccess file like so:
<filesMatch "\.(html|htm|js|css)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 21 Oct 2015 07:28:00 GMT"
</ifModule>
</filesMatch>
I've found this solution works for iOS Safari and other browsers ...
It performs a window.location.reload(true) to force the page to reload avoiding use of caching whenever it detects that the cached web app or web page does not match the server's version.
You really want this mechanism in place before you publish the first version of your site as otherwise once it's out there you can no longer rely on ever upgrading your iOS users. This is a nightmare if you rely on client app and server versions being in sync. Sigh ... Sadly it seems Safari and iOS Safari in particular is eager to take the throne once held by Internet Explorer.
During deployment
Increment and save a build number.
I use a version.json file that looks like {"major":1,"minor":0,"patch":0,"build":12}
Copy version.json to both client and server apps.
On server:
Write a server API in PHP/Node/Ruby/C# that returns the server's version.json
Use web server rules and response headers to prevent API results being cached.
In client app or web page:
To diagnose issues, display the client version + client and server build numbers.
Call on page load ...
function onload() {
var clientVersion = require("./version.json");
// Might replace axios with new fetch() API or older XMLHttpRequest
axios.get("/api/version").then(function(res) {
var serverVersion = res.data;
var lastReload = parseInt(localStorage.getItem("lastReload") || "0");
var now = new Date().getTime();
if (
serverVersion.build !== clientVersion.build &&
now - lastReload > 60 * 1000 // Prevent infinite reloading.
) {
localStorage.setItem("lastReload", now);
window.location.reload(true); // force page reload
}
});
}
You might prefer to test serverVersion.build > clientVersion.build rather than serverVersion.build !== clientVersion.build. The benefit of testing !== is that it allows you not only upgrade the version but also roll back a version and ensure clients get rolled back as well.
In the event that the client and server build numbers are mismatched I prevent the client from infinitely reloading by only performing a single reload within 60 secs.
Note that this method is only concerned with matching build numbers (= deployment number) you might prefer something more sophisticated.
Here's a simple node app to increment a build number.
const APP_DATA = "../app/src/assets"; // client folder
const FUNC_DATA = "../functions/data"; // server folder
var log = require("debug")("build:incr");
var fs = require("fs");
function readJson(path) {
log(`Reading: ${path}`);
const text = fs.readFileSync(path);
return JSON.parse(text);
}
function writeJson(path, json) {
log(`Writing: ${path}`);
fs.writeFileSync(path, JSON.stringify(json));
}
let version = readJson("./version.json");
version.build++;
log("Version = ", version);
writeJson("./version.json", version);
writeJson(`${APP_DATA}/version.json`, version);
writeJson(`${FUNC_DATA}/version.json`, version);
In iOS 10, you can clear the cache by going to Settings > Safari and clicking the "Clear History and Website Data".
For me, this is a bit overkill as I don't want to delete everything for every site. So, what I do is browse directly to the js or css file and reload it if it is the older, cached version.
I don't think there is a "easy" way to do this. I've tried all of the above including modifying css and js query strings. The closest thing that works is in Rockadile's comment.
Open Setting--Safari--Website Data
Then clear the site you are trying to refresh
Tip: If you are debugging a page use the IP Address then it will show up at the top of this list. Keep both safari and the Website data open so you can toggle between the two.
This isn't as simple as ctl-F5 but is certainly less intrusive than clearing the entire browser's cache.
Goto Safari -> Preferences -> click the Advanced tab at the bottom of the Advanced tab click the checkbox that says "Show Develop menu in bar" and close Preferences. Then you can open the Develop menu in the program bar that has Safari, File, Edit, View, etc... in it, and click Empty Caches.

Offline cache - html5

Ok I have a problem with my cache, and just can't figure it out D:
Every time I try to reload the page (To get cache), I get this error: Application Cache Error event: Failed to commit new cache to storage?
Someone who know why this doesn't work?
Links (In case you want to see yourself):
Index.php
Cache file
.htaccess: AddType text/cache-manifest cache
Some files in you cache-manifest don't exist (404).
According to the spec. if not all files can be retrieved the new cache will not be used/committed.
It seems you are using Chrome. Your application cache might be broken. Try clearing it but entering the following to address bar:
chrome://appcache-internals/

Chrome (and possibly other browsers) caching my script include

PROBLEM:
I am hosting a widget on a client's website that will be different for each page on the site.
To render the widget, the client includes a script tag on their pages. This script tag is loaded for every page on the site and the code that it returns depends on the page.
So, if this script gets cached, the end result is that we serve a widget for the wrong page.
Right now, when we serve the script, we set in the response headers
Cache-Control: max-age=0
Expires : 24 hours in the past
yet sometimes browsers still cache the script.
QUESTION:
Is there a way to use http headers to stop caching in all cases or are we going to have to take a completely different approach?
UPDATE:
The headers that topek recommended greatly improved the non-cacheability of the scripts. However, (again in Chrome who seems to be the most cache-aggressive) when using the back, forward, or reload buttons the script is still cached. If you actually CLICK on anything it will be fetched from the server.
It seems that the only foolproof way to stop caching will be to set script sources that are guaranteed to be different for each page load (as suggested by esilija and tejs).
Those two headers should do the trick:
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.setHeader("Expires", "Sat, 26 Jul 1997 05:00:00 GMT");
or you set name according to the current page, e.g. when the user requests the page http://domain/posts/1 then the script name could be http://domain/script/scriptname/posts/1. With approach the script would still be cachable per page.
Do not append a query string on the script like script.js?random_string. Proxies don't play well with this approach. If you want to place a random string in the name, then put it before .js like this script-0934234234.js and rewrite the request on your server.

Categories