Is Safari on iOS 6 caching $.ajax results? - javascript

Since the upgrade to iOS 6, we are seeing Safari's web view take the liberty of caching $.ajax calls. This is in the context of a PhoneGap application so it is using the Safari WebView. Our $.ajax calls are POST methods and we have cache set to false {cache:false}, but still this is happening. We tried manually adding a TimeStamp to the headers but it did not help.
We did more research and found that Safari is only returning cached results for web services that have a function signature that is static and does not change from call to call. For instance, imagine a function called something like:
getNewRecordID(intRecordType)
This function receives the same input parameters over and over again, but the data it returns should be different every time.
Must be in Apple's haste to make iOS 6 zip along impressively they got too happy with the cache settings. Has anyone else seen this behavior on iOS 6? If so, what exactly is causing it?
The workaround that we found was to modify the function signature to be something like this:
getNewRecordID(intRecordType, strTimestamp)
and then always pass in a TimeStamp parameter as well, and just discard that value on the server side. This works around the issue.

After a bit of investigation, turns out that Safari on iOS6 will cache POSTs that have either no Cache-Control headers or even "Cache-Control: max-age=0".
The only way I've found of preventing this caching from happening at a global level rather than having to hack random querystrings onto the end of service calls is to set "Cache-Control: no-cache".
So:
No Cache-Control or Expires headers = iOS6 Safari will cache
Cache-Control max-age=0 and an immediate Expires = iOS6 Safari will cache
Cache-Control: no-cache = iOS6 Safari will NOT cache
I suspect that Apple is taking advantage of this from the HTTP spec in section 9.5 about POST:
Responses to this method are not cacheable, unless the response
includes appropriate Cache-Control or Expires header fields. However,
the 303 (See Other) response can be used to direct the user agent to
retrieve a cacheable resource.
So in theory you can cache POST responses...who knew. But no other browser maker has ever thought it would be a good idea until now. But that does NOT account for the caching when no Cache-Control or Expires headers are set, only when there are some set. So it must be a bug.
Below is what I use in the right bit of my Apache config to target the whole of my API because as it happens I don't actually want to cache anything, even gets. What I don't know is how to set this just for POSTs.
Header set Cache-Control "no-cache"
Update: Just noticed that I didn't point out that it is only when the POST is the same, so change any of the POST data or URL and you're fine. So you can as mentioned elsewhere just add some random data to the URL or a bit of POST data.
Update: You can limit the "no-cache" just to POSTs if you wish like this in Apache:
SetEnvIf Request_Method "POST" IS_POST
Header set Cache-Control "no-cache" env=IS_POST

I hope this can be of use to other developers banging their head against the wall on this one. I found that any of the following prevents Safari on iOS 6 from caching the POST response:
adding [cache-control: no-cache] in the request headers
adding a variable URL parameter such as the current time
adding [pragma: no-cache] in the response headers
adding [cache-control: no-cache] in the response headers
My solution was the following in my Javascript (all my AJAX requests are POST).
$.ajaxSetup({
type: 'POST',
headers: { "cache-control": "no-cache" }
});
I also add the [pragma: no-cache] header to many of my server responses.
If you use the above solution be aware that any $.ajax() calls you make that are set to global: false will NOT use the settings specified in $.ajaxSetup(), so you will need to add the headers in again.

Simple solution for all your web service requests, assuming you're using jQuery:
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
// you can use originalOptions.type || options.type to restrict specific type of requests
options.data = jQuery.param($.extend(originalOptions.data||{}, {
timeStamp: new Date().getTime()
}));
});
Read more about the jQuery prefilter call here.
If you aren't using jQuery, check the docs for your library of choice. They may have similar functionality.

I just had this issue as well in a PhoneGap application. I solved it by using the JavaScript function getTime() in the following manner:
var currentTime = new Date();
var n = currentTime.getTime();
postUrl = "http://www.example.com/test.php?nocache="+n;
$.post(postUrl, callbackFunction);
I wasted a few hours figuring this out. It would have been nice of Apple to notify developers of this caching issue.

I had the same problem with a webapp getting data from ASP.NET webservice
This worked for me:
public WebService()
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
...
}

Finally, I've a solution to my uploading problem.
In JavaScript:
var xhr = new XMLHttpRequest();
xhr.open("post", 'uploader.php', true);
xhr.setRequestHeader("pragma", "no-cache");
In PHP:
header('cache-control: no-cache');

From my own blog post iOS 6.0 caching Ajax POST requests:
How to fix it: There are various methods to prevent caching of requests. The recommended method is adding a no-cache header. This is how it is done.
jQuery:
Check for iOS 6.0 and set Ajax header like this:
$.ajaxSetup({ cache: false });
ZeptoJS:
Check for iOS 6.0 and set the Ajax header like this:
$.ajax({
type: 'POST',
headers : { "cache-control": "no-cache" },
url : ,
data:,
dataType : 'json',
success : function(responseText) {…}
Server side
Java:
httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
Make sure to add this at the top the page before any data is sent to the client.
.NET
Response.Cache.SetNoStore();
Or
Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
PHP
header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1.
header('Pragma: no-cache'); // HTTP 1.0.

This JavaScript snippet works great with jQuery and jQuery Mobile:
$.ajaxSetup({
cache: false,
headers: {
'Cache-Control': 'no-cache'
}
});
Just place it somewhere in your JavaScript code (after jQuery is loaded, and best before you do AJAX requests) and it should help.

You can also fix this issue by modifying the jQuery Ajax function by doing the following (as of 1.7.1) to the top of the Ajax function (function starts at line 7212). This change will activate the built-in anti-cache feature of jQuery for all POST requests.
(The full script is available at http://dl.dropbox.com/u/58016866/jquery-1.7.1.js.)
Insert below line 7221:
if (options.type === "POST") {
options.cache = false;
}
Then modify the following (starting at line ~7497).
if (!s.hasContent) {
// If data is available, append data to URL
if (s.data) {
s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
// #9682: remove data so that it's not used in an eventual retry
delete s.data;
}
// Get ifModifiedKey before adding the anti-cache parameter
ifModifiedKey = s.url;
// Add anti-cache in URL if needed
if (s.cache === false) {
var ts = jQuery.now(),
// Try replacing _= if it is there
ret = s.url.replace(rts, "$1_=" + ts);
// If nothing was replaced, add timestamp to the end.
s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
}
}
To:
// More options handling for requests with no content
if (!s.hasContent) {
// If data is available, append data to URL
if (s.data) {
s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
// #9682: remove data so that it's not used in an eventual retry
delete s.data;
}
// Get ifModifiedKey before adding the anti-cache parameter
ifModifiedKey = s.url;
}
// Add anti-cache in URL if needed
if (s.cache === false) {
var ts = jQuery.now(),
// Try replacing _= if it is there
ret = s.url.replace(rts, "$1_=" + ts);
// If nothing was replaced, add timestamp to the end.
s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
}

A quick work-around for GWT-RPC services is to add this to all the remote methods:
getThreadLocalResponse().setHeader("Cache-Control", "no-cache");

This is an update of Baz1nga's answer. Since options.data is not an object but a string I just resorted to concatenating the timestamp:
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (originalOptions.type == "post" || options.type == "post") {
if (options.data && options.data.length)
options.data += "&";
else
options.data = "";
options.data += "timeStamp=" + new Date().getTime();
}
});

In order to resolve this issue for WebApps added to the home screen, both of the top voted workarounds need to be followed. Caching needs to be turned off on the webserver to prevent new requests from being cached going forward and some random input needs to be added to every post request in order for requests that have already been cached to go through. Please refer to my post:
iOS6 - Is there a way to clear cached ajax POST requests for webapp added to home screen?
WARNING: to anyone who implemented a workaround by adding a timestamp to their requests without turning off caching on the server. If your app is added to the home screen, EVERY post response will now be cached, clearing safari cache doesn't clear it and it doesn't seem to expire. Unless someone has a way to clear it, this looks like a potential memory leak!

Things that DID NOT WORK for me with an iPad 4/iOS 6:
My request containing: Cache-Control:no-cache
//asp.net's:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache)
Adding cache: false to my jQuery ajax call
$.ajax(
{
url: postUrl,
type: "POST",
cache: false,
...
Only this did the trick:
var currentTime = new Date();
var n = currentTime.getTime();
postUrl = "http://www.example.com/test.php?nocache="+n;
$.post(postUrl, callbackFunction);

That's the work around for GWT-RPC
class AuthenticatingRequestBuilder extends RpcRequestBuilder
{
#Override
protected RequestBuilder doCreate(String serviceEntryPoint)
{
RequestBuilder requestBuilder = super.doCreate(serviceEntryPoint);
requestBuilder.setHeader("Cache-Control", "no-cache");
return requestBuilder;
}
}
AuthenticatingRequestBuilder builder = new AuthenticatingRequestBuilder();
((ServiceDefTarget)myService).setRpcRequestBuilder(builder);

My workaround in ASP.NET (pagemethods, webservice, etc.)
protected void Application_BeginRequest(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
}

While adding cache-buster parameters to make the request look different seems like a solid solution, I would advise against it, as it would hurt any application that relies on actual caching taking place. Making the APIs output the correct headers is the best possible solution, even if that's slightly more difficult than adding cache busters to the callers.

For those that use Struts 1, here is how I fixed the issue.
web.xml
<filter>
<filter-name>SetCacheControl</filter-name>
<filter-class>com.example.struts.filters.CacheControlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SetCacheControl</filter-name>
<url-pattern>*.do</url-pattern>
<http-method>POST</http-method>
</filter-mapping>
com.example.struts.filters.CacheControlFilter.js
package com.example.struts.filters;
import java.io.IOException;
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
public class CacheControlFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Expires", "Mon, 18 Jun 1973 18:00:00 GMT");
resp.setHeader("Last-Modified", new Date().toString());
resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
resp.setHeader("Pragma", "no-cache");
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}

I was able to fix my problem by using a combination of $.ajaxSetup and appending a timestamp to the url of my post (not to the post parameters/body). This based on the recommendations of previous answers
$(document).ready(function(){
$.ajaxSetup({ type:'POST', headers: {"cache-control","no-cache"}});
$('#myForm').submit(function() {
var data = $('#myForm').serialize();
var now = new Date();
var n = now.getTime();
$.ajax({
type: 'POST',
url: 'myendpoint.cfc?method=login&time='+n,
data: data,
success: function(results){
if(results.success) {
window.location = 'app.cfm';
} else {
console.log(results);
alert('login failed');
}
}
});
});
});

I think you have already resolved your issue, but let me share an idea about web caching.
It is true you can add many headers in each language you use, server side, client side, and you can use many other tricks to avoid web caching, but always think that you can never know from where the client are connecting to your server, you never know if he are using a Hotel “Hot-Spot” connection that uses Squid or other caching products.
If the users are using proxy to hide his real position, etc… the real only way to avoid caching is the timestamp in the request also if is unused.
For example:
/ajax_helper.php?ts=3211321456
Then every cache manager you have to pass didnt find the same URL in the cache repository and go re-download the page content.

Depending on the app you can trouble shoot the issue now in iOS 6 using Safari>Advanced>Web Inspector so that is helpful with this situation.
Connect the phone to Safari on a Mac an then use the developer menu to trouble shoot the web app.
Clear the website data on the iPhone after update to iOS6, including specific to the app using a Web View. Only one app had an issue and this solved it during IOS6 Beta testing way back, since then no real problems.
You may need to look at your app as well, check out NSURLCache if in a WebView in a custom app.
https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003754
I guess depending on the true nature of your problem, implementation, etc. ..
Ref: $.ajax calls

I found one workaround that makes me curious as to why it works. Before reading Tadej's answer concerning ASP.NET web service, I was trying to come up with something that would work.
And I'm not saying that it's a good solution, but I just wanted to document it here.
main page: includes a JavaScript function, checkStatus(). The method calls another method which uses a jQuery AJAX call to update the html content. I used setInterval to call checkStatus(). Of course, I ran into the caching problem.
Solution: use another page to call the update.
On the main page, I set a boolean variable, runUpdate, and added the following to the body tag:
<iframe src="helper.html" style="display: none; visibility: hidden;"></iframe>
In the of helper.html:
<meta http-equiv="refresh" content="5">
<script type="text/javascript">
if (parent.runUpdate) { parent.checkStatus(); }
</script>
So, if checkStatus() is called from the main page, I get the cached content. If I call checkStatus from the child page, I get updated content.

While my login and signup pages works like a charm in Firefox, IE and Chrome... I've been struggling with this issue in Safari for IOS and OSX, few months ago I found a workaround on the SO.
<body onunload="">
OR via javascript
<script type="text/javascript">
window.onunload = function(e){
e.preventDefault();
return;
};
</script>
This is kinda ugly thing but works for a while.
I don't know why, but returning null to the onunload event the page do not get cached in Safari.

We found that older iPhones and iPads, running iOS versions 9 & 10, occasionally return bogus blank AJAX results, perhaps due to Apple's turning down CPU speed. When returning the blank result, iOS does not call the server, as if returning a result from cache. Frequency varies widely, from roughly 10% to 30% of AJAX calls return blank.
The solution is hard to believe. Just wait 1s and call again. In our testing, only one repeat was all that was ever needed, but we wrote the code to call up to 4 times. We're not sure if the 1s wait is required, but we didn't want to risk burdening our server with bursts of repeated calls.
We found the problem happened with two different AJAX calls, calling on different API files with different data. But I'm concerned it could happen on any AJAX call. We just don't know because we don't inspect every AJAX result and we don't test every call multiple times on old devices.
Both problem AJAX calls were using: POST, Asynchronously = true, setRequestHeader = ('Content-Type', 'application/x-www-form-urlencoded')
When the problem happens, there's usually only one AJAX call going on. So it's not due to overlapping AJAX calls. Sometimes the problem happens when the device is busy, but sometimes not, and without DevTools we don't really know what's happening at the time.
iOS 13 doesn't do this, nor Chrome or Firefox. We don't have any test devices running iOS 11 or 12. Perhaps someone else could test those?
I'm noting this here because this question is the top Google result when searching for this problem.

It worked with ASP.NET only after adding the pragma:no-cache header in IIS. Cache-Control: no-cache was not enough.

I suggest a workaround to modify the function signature to be something like this:
getNewRecordID(intRecordType, strTimestamp)
and then always pass in a TimeStamp parameter as well, and just discard that value on the server side. This works around the issue.

Related

audio duration in Safari always returning infinity when streaming through PHP

For some reason in Safari (and no other major browser), when I serve an MP3 via PHP through an Audio context in JavaScript, the duration of the MP3 is always returned as infinity.
This problem has stumped me for the last few days and after reading a few links (including this one) in search for a solution, I have not progressed at all.
My code
PHP:
$path = "path/to/file.mp3";
$file = [
"path" => $path,
"size" => filesize($path),
"bitrate" => $bitrate
];
header("Accept-Ranges: bytes", false);
header("Content-Length: " . $file["size"], false);
header("Content-Type: audio/mpeg", false);
echo file_get_contents($file["path"]);
exit;
JavaScript:
var audio = new Audio(url);
// returns infinite on Safari
// returns 312.27311 on Chrome and Firefox (which is correct)
console.log(audio.duration);
I'm still yet to figure out why this problem is only in Safari and what is causing it in the first place, so if anyone has a solution it would be much appreciated!
Cheers.
After much investigation like yourself I finally figured it out.
The first request Safari sends has the header:
Range: bytes=0-1
It's your job to respond "correctly" so that Safari will send additional requests to fetch the rest of the file.
Here is my example of the "correct" headers:
Content-Length: 2
Accept-Ranges: bytes
Content-Range: bytes 0-1/8469
(Make sure you set response status to 206!)
(8469 is the Content-Length of the entire file)
What happens after that is somewhat magical - Safari sends a follow up request that has this header:
Range: bytes=0-8468
And you should respond "correctly" like with these headers:
Content-Length: 8469
Accept-Ranges: bytes
Content-Range: bytes 0-8468/8469
(Again, status 206!)
I hope this solves it for you, since I spent many hours searching to no avail. I eventually realized I needed to adjust my server to handle requests where the Range header is present.
I used this commit to help me understand what the "correct" response is: https://github.com/jooby-project/jooby/commit/142a933a31b9d8742714ecd38475d90e563314f7
As this thread is popping up first in google, allow me to share my experience.
Like many, I'm generating the mp3 through a php page, mostly in order to "hide" the real link of the mp3. And for this, Safari is quite annoying. To make it short, Safari considers the sound as a "live broadcast", ending up having this "infinite" duration. For the details, this post explained quite well why.
So what to do ? You have to put conditions on the headers of your php page that generate the sound (as mentioned by #brianarpie). Here is my php-generating-mp3 page :
<?php
// first, you need this function :
function serveFilePartial($fileName, $fileTitle = null, $contentType = 'application/octet-stream'){
// the code of this can be found here : https://github.com/pomle/php-serveFilePartial/blob/master/ServeFilePartial.inc.php
}
$link_of_your_mp3 = "var/my_folder/my_music.mp3"; // this is not an URL, but an absolute path
// and we call the function :
serveFilePartial($link_of_your_mp3, null, 'audio/mpeg');
?>
.. and that's it ! this function will generate the right headers depending on what the web-browser is asking for.
Of course you've done all your checking on link, vars, etc to protect the page. Note that my example is mp3, so I have to call the function with 'audio/mpeg'. If you output a different fileformat, you will have to adapt the content Type of the function.

online offline check using javascript [duplicate]

This question already has answers here:
Detect the Internet connection is offline?
(22 answers)
Closed 8 years ago.
How do you check if there is an internet connection using jQuery? That way I could have some conditionals saying "use the google cached version of JQuery during production, use either that or a local version during development, depending on the internet connection".
The best option for your specific case might be:
Right before your close </body> tag:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.10.2.min.js"><\/script>')</script>
This is probably the easiest way given that your issue is centered around jQuery.
If you wanted a more robust solution you could try:
var online = navigator.onLine;
Read more about the W3C's spec on offline web apps, however be aware that this will work best in modern web browsers, doing so with older web browsers may not work as expected, or at all.
Alternatively, an XHR request to your own server isn't that bad of a method for testing your connectivity. Considering one of the other answers state that there are too many points of failure for an XHR, if your XHR is flawed when establishing it's connection then it'll also be flawed during routine use anyhow. If your site is unreachable for any reason, then your other services running on the same servers will likely be unreachable also. That decision is up to you.
I wouldn't recommend making an XHR request to someone else's service, even google.com for that matter. Make the request to your server, or not at all.
What does it mean to be "online"?
There seems to be some confusion around what being "online" means. Consider that the internet is a bunch of networks, however sometimes you're on a VPN, without access to the internet "at-large" or the world wide web. Often companies have their own networks which have limited connectivity to other external networks, therefore you could be considered "online". Being online only entails that you are connected to a network, not the availability nor reachability of the services you are trying to connect to.
To determine if a host is reachable from your network, you could do this:
function hostReachable() {
// Handle IE and more capable browsers
var xhr = new ( window.ActiveXObject || XMLHttpRequest )( "Microsoft.XMLHTTP" );
// Open new request as a HEAD to the root hostname with a random param to bust the cache
xhr.open( "HEAD", "//" + window.location.hostname + "/?rand=" + Math.floor((1 + Math.random()) * 0x10000), false );
// Issue request and handle response
try {
xhr.send();
return ( xhr.status >= 200 && (xhr.status < 300 || xhr.status === 304) );
} catch (error) {
return false;
}
}
You can also find the Gist for that here: https://gist.github.com/jpsilvashy/5725579
Details on local implementation
Some people have commented, "I'm always being returned false". That's because you're probably testing it out on your local server. Whatever server you're making the request to, you'll need to be able to respond to the HEAD request, that of course can be changed to a GET if you want.
Ok, maybe a bit late in the game but what about checking with an online image?
I mean, the OP needs to know if he needs to grab the Google CMD or the local JQ copy, but that doesn't mean the browser can't read Javascript no matter what, right?
<script>
function doConnectFunction() {
// Grab the GOOGLE CMD
}
function doNotConnectFunction() {
// Grab the LOCAL JQ
}
var i = new Image();
i.onload = doConnectFunction;
i.onerror = doNotConnectFunction;
// CHANGE IMAGE URL TO ANY IMAGE YOU KNOW IS LIVE
i.src = 'http://gfx2.hotmail.com/mail/uxp/w4/m4/pr014/h/s7.png?d=' + escape(Date());
// escape(Date()) is necessary to override possibility of image coming from cache
</script>
Just my 2 cents
5 years later-version:
Today, there are JS libraries for you, if you don't want to get into the nitty gritty of the different methods described on this page.
On of these is https://github.com/hubspot/offline. It checks for the connectivity of a pre-defined URI, by default your favicon. It automatically detects when the user's connectivity has been reestablished and provides neat events like up and down, which you can bind to in order to update your UI.
You can mimic the Ping command.
Use Ajax to request a timestamp to your own server, define a timer using setTimeout to 5 seconds, if theres no response it try again.
If there's no response in 4 attempts, you can suppose that internet is down.
So you can check using this routine in regular intervals like 1 or 3 minutes.
That seems a good and clean solution for me.
You can try by sending XHR Requests a few times, and then if you get errors it means there's a problem with the internet connection.
I wrote a jQuery plugin for doing this. By default it checks the current URL (because that's already loaded once from the Web) or you can specify a URL to use as an argument. Always doing a request to Google isn't the best idea because it's blocked in different countries at different times. Also you might be at the mercy of what the connection across a particular ocean/weather front/political climate might be like that day.
http://tomriley.net/blog/archives/111
i have a solution who work here to check if internet connection exist :
$.ajax({
url: "http://www.google.com",
context: document.body,
error: function(jqXHR, exception) {
alert('Offline')
},
success: function() {
alert('Online')
}
})
Sending XHR requests is bad because it could fail if that particular server is down. Instead, use googles API library to load their cached version(s) of jQuery.
You can use googles API to perform a callback after loading jQuery, and this will check if jQuery was loaded successfully. Something like the code below should work:
<script type="text/javascript">
google.load("jquery");
// Call this function when the page has been loaded
function test_connection() {
if($){
//jQuery WAS loaded.
} else {
//jQuery failed to load. Grab the local copy.
}
}
google.setOnLoadCallback(test_connection);
</script>
The google API documentation can be found here.
A much simpler solution:
<script language="javascript" src="http://maps.google.com/maps/api/js?v=3.2&sensor=false"></script>
and later in the code:
var online;
// check whether this function works (online only)
try {
var x = google.maps.MapTypeId.TERRAIN;
online = true;
} catch (e) {
online = false;
}
console.log(online);
When not online the google script will not be loaded thus resulting in an error where an exception will be thrown.

how to clear the cache on window.location.replace()? [duplicate]

I am looking for a way to programmatically empty the browser cache. I am doing this because the application caches confidential data and I'd like to remove those when you press "log out". This would happen either via server or JavaScript. Of course, using the software on foreign/public computer is still discouraged as there are more dangers like key loggers that you just can't defeat on software level.
There's no way a browser will let you clear its cache. It would be a huge security issue if that were possible. This could be very easily abused - the minute a browser supports such a "feature" will be the minute I uninstall it from my computer.
What you can do is to tell it not to cache your page, by sending the appropriate headers or using these meta tags:
<meta http-equiv='cache-control' content='no-cache'>
<meta http-equiv='expires' content='0'>
<meta http-equiv='pragma' content='no-cache'>
You might also want to consider turning off auto-complete on form fields, although I'm afraid there's a standard way to do it (see this question).
Regardless, I would like to point out that if you are working with sensitive data you should be using SSL. If you aren't using SSL, anyone with access to the network can sniff network traffic and easily see what your user is seeing.
Using SSL also makes some browsers not use caching unless explicitly told to. See this question.
It's possible, you can simply use jQuery to substitute the 'meta tag' that references the cache status with an event handler / button, and then refresh, easy,
$('.button').click(function() {
$.ajax({
url: "",
context: document.body,
success: function(s,x){
$('html[manifest=saveappoffline.appcache]').attr('content', '');
$(this).html(s);
}
});
});
NOTE: This solution relies on the Application Cache that is implemented as part of the HTML 5 spec. It also requires server configuration to set up the App Cache manifest. It does not describe a method by which one can clear the 'traditional' browser cache via client- or server-side code, which is nigh impossible to do.
use html itself.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.
The best idea is to make js file generation with name + some hash with version, if you do need to clear cache, just generate new files with new hash, this will trigger browser to load new files
Here is a single-liner of how you can delete ALL browser network cache using Cache.delete()
caches.keys().then((keyList) => Promise.all(keyList.map((key) => caches.delete(key))))
Works on Chrome 40+, Firefox 39+, Opera 27+ and Edge.
Initially I tried various programmatic approach in my html, JS to clear browser cache. Nothing works on latest Chrome.
Finally, I ended up with .htaccess:
<IfModule mod_headers.c>
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires 0
</IfModule>
Tested in Chrome, Firefox, Opera
Reference: https://wp-mix.com/disable-caching-htaccess/
On Chrome, you should be able to do this using the benchmarking extension. You need to start your chrome with the following switches:
./chrome --enable-benchmarking --enable-net-benchmarking
In Chrome's console now you can do the following:
chrome.benchmarking.clearCache();
chrome.benchmarking.clearHostResolverCache();
chrome.benchmarking.clearPredictorCache();
chrome.benchmarking.closeConnections();
As you can tell from above commands, it not only clears the browser cache, but also clears the DNS cache and closes network connections. These are great when you're doing page load time benchmarking. Obviously you don't have to use them all if not needed (e.g. clearCache() should suffice if you need to clear the cache only and don't care about DNS cache and connections).
You can now use Cache.delete()
Example:
let id = "your-cache-id";
// you can find the id by going to
// application>storage>cache storage
// (minus the page url at the end)
// in your chrome developer console
caches.open(id)
.then(cache => cache.keys()
.then(keys => {
for (let key of keys) {
cache.delete(key)
}
}));
Works on Chrome 40+, Firefox 39+, Opera 27+ and Edge.
You could have the server respond with a Clear Site Data directive that instructs the user agent to clear the site's locally stored data.
For example:
Clear-Site-Data: "cache", "cookies", "storage"
That header would instruct the user agent to clear all locally stored data, including:
Network cache
User agent caches (like prerendered pages, script caches, etc.)
Cookies
HTTP authentication credentials
Origin-bound tokens (such as Channel ID and Token Binding)
Local storage
Session storage
IndexedDB
Web SQL database
Service Worker registrations
You can send the request using fetch() and do location.reload() afterwards to get a fresh restart.
location.reload(true); will hard reload the current page, ignoring the cache.
Cache.delete() can also be used for new chrome, firefox and opera.
//The code below should be put in the "js" folder with the name "clear-browser-cache.js"
(function () {
var process_scripts = false;
var rep = /.*\?.*/,
links = document.getElementsByTagName('link'),
scripts = document.getElementsByTagName('script');
var value = document.getElementsByName('clear-browser-cache');
for (var i = 0; i < value.length; i++) {
var val = value[i],
outerHTML = val.outerHTML;
var check = /.*value="true".*/;
if (check.test(outerHTML)) {
process_scripts = true;
}
}
for (var i = 0; i < links.length; i++) {
var link = links[i],
href = link.href;
if (rep.test(href)) {
link.href = href + '&' + Date.now();
}
else {
link.href = href + '?' + Date.now();
}
}
if (process_scripts) {
for (var i = 0; i < scripts.length; i++) {
var script = scripts[i],
src = script.src;
if (src !== "") {
if (rep.test(src)) {
script.src = src + '&' + Date.now();
}
else {
script.src = src + '?' + Date.now();
}
}
}
}
})();
At the end of the tah head, place the line at the code below
< script name="clear-browser-cache" src='js/clear-browser-cache.js' value="true" >< /script >
By definining a function for cache invalidate meta tags:
function addMetaTag(name,content){
var meta = document.createElement('meta');
meta.httpEquiv = name;
meta.content = content;
document.getElementsByTagName('head')[0].appendChild(meta);
}
You can call:
addMetaTag("pragma","no-cache")
addMetaTag("expires","0")
addMetaTag("cache-control","no-cache")
That will insert meta tags for subsequents requests, which will force browser to fetch fresh content. After inserting, you can call location.reload() and this will work in mostly all browsers (Cache.delete() is not working at chrome for ex.)
I clear the browser's cache for development reasons. Clearing local storage, session storage, IndexDB, cookies, etc. when the data schema changes. If not cleared, there could be data corruption when syncing data with the database. Cache could also be cleared for security reasons as the OP suggested.
This is the code I use:
caches.keys().then(list => list.map(key => caches.delete(key)))
Simple as that, works like a champ. For more information:
https://developer.mozilla.org/en-US/docs/Web/API/Cache
Imagine the .js files are placed in /my-site/some/path/ui/js/myfile.js
So normally the script tag would look like:
<script src="/my-site/some/path/ui/js/myfile.js"></script>
Now change that to:
<script src="/my-site/some/path/ui-1111111111/js/myfile.js"></script>
Now of course that will not work. To make it work you need to add one or a few lines to your .htaccess
The important line is: (entire .htaccess at the bottom)
RewriteRule ^my-site\/(.*)\/ui\-([0-9]+)\/(.*) my-site/$1/ui/$3 [L]
So what this does is, it kind of removes the 1111111111 from the path and links to the correct path.
So now if you make changes you just have to change the number 1111111111 to whatever number you want. And however you include your files you can set that number via a timestamp when the js-file has last been modified. So cache will work normally if the number does not change. If it changes it will serve the new file (YES ALWAYS) because the browser get's a complete new URL and just believes that file is so new he must go get it.
You can use this for CSS, favicons and what ever gets cached. For CSS just use like so
<link href="http://my-domain.com/my-site/some/path/ui-1492513798/css/page.css" type="text/css" rel="stylesheet">
And it will work! Simple to update, simple to maintain.
The promised full .htaccess
If you have no .htaccess yet this is the minimum you need to have there:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^my-site\/(.*)\/ui\-([0-9]+)\/(.*) my-site/$1/ui/$3 [L]
</IfModule>

Javascript check file size

Is it possible to keep checking with javascript if the filesize of a file on a webserver (e.g. http://www.mysite.com/myfile.js) is larger than 0 bytes and if so return a true or false value?
Thanks in advance!
Theoretically, you could use XHR to issue a HTTP HEAD request and check the Content-Length in the response headers.
A HEAD request is identical to a regular GET request except the server MUST NOT return the actual content. In other words, the server replies with the headers it would have had you tried to GET the resource, but then stops and does not send the file.
However, some severs respond to a HEAD request with a Content-Length header of 0, regardless of the actual size of the file. Others respond with the size of the file.
In order to accomplish this, you'll have to pray your server returns a file's actual size to a HEAD request.
If it does, getting that value is easy:
$.ajax('/myfile.js', {
type: 'HEAD',
success: function(d,r,xhr) {
fileSize = xhr.getResponseHeader('Content-Length');
}
});
Note that JSFiddle's server always returns 0 when we HEAD /, even though / is 16916 bytes.
Also note what jQuery's docs say about the HTTP request type option:
The type of request to make ("POST" or "GET"), default is "GET". Note: Other HTTP request methods, such as PUT and DELETE, can also be used here, but they are not supported by all browsers.
I just tested this Fiddle in IE 6-10, Firefox 3.6-7, Opera 9-11, and Chrome, and every single browser correctly issued the HEAD request, so I wouldn't worry about that vague incompatibility statement. Of more concern is how your server responds.
Javascript has no access to the filesystem (thankfully) so I'm afraid not.
The idea given by josh3736 is great. Only the code example he gave, refused to work in my browser (Chrome 20, Firefox 13, IE 8, Opera 12), for the reason, that I don't know.
Here is the one, that worked perfectly in my case:
jQuery.ajax
({
cache: false,
type: 'HEAD',
url: 'myfile.js',
success: function(d,r,xhr){alert('File size is ' + xhr.getResponseHeader('Content-Length') + ' bytes.')},
error: function(xhr, desc, er){alert('ERROR: "' + xhr.responseText + '"')}
});
I want also to notice, that Apache on-board XAMPP server works just fine with HEAD request, but of course, when run on localhost, such request is blocked by a browser with error message: "Origin localhost is not allowed by Access-Control-Allow-Origin" (example from Chrome).
IE:
If you set the Ie 'Document Mode' to 'Standards' you can use the simple javascript 'size' method to get the uploaded file's size.
Set the Ie 'Document Mode' to 'Standards':
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
Than, use the 'size' javascript method to get the uploaded file's size:
<script type="text/javascript">
var uploadedFile = document.getElementById('imageUpload');
var fileSize = uploadedFile.files[0].size;
alert(fileSize);
</script>
Other browsers:
On all the other browsers you can use the javascript 'size' method without any problems.

Access denied to jQuery script on IE

I have an iframe using the jQuery 1.4.2 script. The same iframe is injected into both http and https sites. The jQuery script is included in the main HTML file as a relative path (e.g., /scripts/jquery-1.4.2.min.js).
When an AJAX call is made, Internet Explorer denies access. The AJAX is calling on another subdomain, but it's using the right protocol. All other browsers work but Internet Explorer gives the following error:
SCRIPT5: Access is denied.
jquery-1.4.2.min.js, line 127 character 344
I heard this error is from cross-domain AJAX calls. But why is IE the only one giving me crap? Is there an IE solution?
Also, this is my AJAX:
$.ajax({
url: thisURL,
dataType: "json",
data: {cmd : 'getMessage', uurl: urlVar, t: Math.random()},
success: function(ret){
callback(ret)
}
});
IE requires you to use XDomainRequest instead of XHR for cross site, you can try something like...
if ($.browser.msie && window.XDomainRequest) {
// Use Microsoft XDR
var xdr = new XDomainRequest();
xdr.open("get", url);
xdr.onload = function() {
// XDomainRequest doesn't provide responseXml, so if you need it:
var dom = new ActiveXObject("Microsoft.XMLDOM");
dom.async = false;
dom.loadXML(xdr.responseText);
};
xdr.send();
} else {
// your ajax request here
$$.ajax({
url: thisURL,
dataType: "json",
data: {cmd : 'getMessage', uurl: urlVar, t: Math.random()},
success: function(ret){
callback(ret)
}
});
}
Reference
http://forum.jquery.com/topic/cross-domain-ajax-and-ie
not sure whether it fits your scenario
xdr = new XDomainRequest();
xdr.onload=function()
{
alert(xdr.responseText);
}
xdr.open("GET", thisUrl); //thisURl ->your cross domain request URL
//pass your data here
xdr.send([data]);
you can find some more guidance here
This solved the issue gracefully for me:
https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest
Just install/compile after jQuery and before your script and use the $.ajax method as you normally would, the rest is handled behind the automatically.
Have you try to use the lastest of JQuery(> jquery-1.8.0)? Since the version 1.8.0, they solved some IE9's bugs. Perhaps this one too.
http://blog.jquery.com/2012/08/30/jquery-1-8-1-released/
I had a similar problem and the solution for me was to use jsonp instead of json. That way I didn't have to break out a customer version for IE.
You can only do this if the json server host supports the callback request variable or you have access to the server and can add support. Here is a page that helped me understand the process. Its .net mvc focused, but it gives a good over view of the diffrence between json and jsonp.
http://blogorama.nerdworks.in/entry-EnablingJSONPcallsonASPNETMVC.aspx
Check the domain you are accessing, following response headers should be there
"Access-Control-Allow-Methods" : "POST, GET, OPTIONS"
"Access-Control-Allow-Origin" : "http://www.mydomain.com" or "*"
the other domain should allow your script request. One more header to be added to your response is P3P header.
"p3p" : "CP=IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"
it should help you out.
I was facing similar issue.
I was using file upload control but it was hidden and I had another element trying to control the file upload and events to upload file in ajax way
try using the file upload control directly. this solved issue in my application.
I get this bug (and thus google here) but the reason was very different. So if you don't have cross site and still get this access denied error: double check the value sent
let's say that you affect one of you variable with the bad following expression:
urlVar = $("theID").val // without () this was the error!
[...]ajax call:
data: {cmd : 'getMessage', uurl: urlVar, t: Math.random()},
Google/FF have no problem with this (check what is receive server side...) BUT IE refuse to send this!
I changed my JQuery from version 1.10.1 to 1.10.2 and it seems to have solved this problem for me.
It seems that MS is finding its own way of doing things, rather than adopting industry recommendations. I found the solution here:
https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest/blob/master/jQuery.XDomainRequest.js
Simply add 'callback=?' on your ajax URL request like here:
http://wsvdmeer.blogspot.com.es/2012/08/bugfix-getjson-not-working-in-ie.html

Categories

Resources