Related
Is it possible to determine if Google Chrome is in incognito mode via a script?
Edit: I actually meant is it possible via user-script, but the answers assume JavaScript is running on a web page. I've re-asked the question here in regards to user scripts.
The functionality of this answer is Chrome version dependant. The most recent comment was this works in v90
Yes. The FileSystem API is disabled in incognito mode. Check out https://jsfiddle.net/w49x9f1a/ when you are and aren't in incognito mode.
Sample code:
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
console.log("check failed?");
} else {
fs(window.TEMPORARY,
100,
console.log.bind(console, "not in incognito mode"),
console.log.bind(console, "incognito mode"));
}
In Chrome 74 to 84.0.4147.135 you can determine this by estimating the available file system storage space
See the jsfiddle
if ('storage' in navigator && 'estimate' in navigator.storage) {
const {usage, quota} = await navigator.storage.estimate();
console.log(`Using ${usage} out of ${quota} bytes.`);
if(quota < 120000000){
console.log('Incognito')
} else {
console.log('Not Incognito')
}
} else {
console.log('Can not detect')
}
One way is to visit a unique URL and then check to see whether a link to that URL is treated as visited by CSS.
You can see an example of this in "Detecting Incognito" (Dead link).
Research paper by same author to replace Detecting Incognito link above
In main.html add an iframe,
<iframe id='testFrame' name='testFrame' onload='setUniqueSource(this)' src='' style="width:0; height:0; visibility:hidden;"></iframe>
, and some JavaScript code:
function checkResult() {
var a = frames[0].document.getElementById('test');
if (!a) return;
var color;
if (a.currentStyle) {
color = a.currentStyle.color;
} else {
color = frames[0].getComputedStyle(a, '').color;
}
var visited = (color == 'rgb(51, 102, 160)' || color == '#3366a0');
alert('mode is ' + (visited ? 'NOT Private' : 'Private'));
}
function setUniqueSource(frame) {
frame.src = "test.html?" + Math.random();
frame.onload = '';
}
Then in test.html that are loaded into the iFrame:
<style>
a:link { color: #336699; }
a:visited { color: #3366A0; }
</style>
<script>
setTimeout(function() {
var a = document.createElement('a');
a.href = location;
a.id = 'test';
document.body.appendChild(a);
parent.checkResult();
}, 100);
</script>
NOTE: trying this from the filesystem can make Chrome cry about "Unsafe Javascript". It
will, however, work serving from a webserver.
You can, in JavaScript, see JHurrah's answer. Except for not highlighting links, all incognito mode does is not save browse history and cookies. From google help page:
Webpages that you open and files downloaded while you are incognito
aren't recorded in your browsing and
download histories.
All new cookies are deleted after you close all incognito windows
that you've opened.
As you can see the differences between normal browsing and incognito happen after you visit the webpage, hence there is nothing that browser communicates to the server when it's in this mode.
You can see what exactly your browser sends to the server using one of many HTTP request analysers, like this one here. Compare the headers between normal session and incognito and you will see no difference.
If you are developing an Extension then you can use the tabs API to determine if a window/tab incognito.
More information can be found here.
If you are just working with a webpage, it is not easy, and it is designed to be that way. However, I have noticed that all attempts to open a database (window.database) fail when in incongnito, this is because when in incognito no trace of data is allowed to be left on the users machine.
I haven't tested it but I suspect all calls to localStorage fail too.
For those looking for a solution, here's a brief rundown of the current methods of detecting Private Browsing modes in various browsers as of October 2021:
Chromium: Similar to Vinnie James's answer, call navigator.storage.estimate(), grab the quota property and compare it to performance.memory.jsHeapSizeLimit. If the quota property is less than jsHeapSizeLimit, it's incognito. If jsHeapSizeLimit is undefined, use 1073741824 (1 GiB).
Safari for macOS: Use safari.pushNotification.requestPermission on a non-existent push server & grab the error. If "gesture" does not appear in the error, it's in private mode.
Safari for iOS: Create an iframe & add an error event listener using contentWindow.applicationCache on the iframe. If the error trips, it's in private mode.
Firefox: navigator.serviceWorker will be undefined in a private window.
Internet Explorer: window.indexedDB will be undefined in InPrivate mode.
You can see an implementation of these methods in the detectIncognito script I have available on GitHub.
Update This seems to not be working anymore
This uses a promise to wait for the asynchronous code to set a flag, so we can use it synchronously afterward.
let isIncognito = await new Promise((resolve, reject)=>{
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) reject('Check incognito failed');
else fs(window.TEMPORARY, 100, ()=>resolve(false), ()=>resolve(true));
});
then we can do
if(isIncognito) alert('in incognito');
else alert('not in incognito');
Note, to use await you need to be inside an async function. If you're not, you can wrap all your code inside one to be able to
Quick function based on Alok's Answer (note: this is asynchronous)
Update - not working anymore
function ifIncognito(incog,func){
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) console.log("checking incognito failed");
else {
if(incog) fs(window.TEMPORARY, 100, ()=>{}, func);
else fs(window.TEMPORARY, 100, func, ()=>{});
}
}
usage:
ifIncognito(true, ()=>{ alert('in incognito') });
// or
ifIncognito(false, ()=>{ alert('not in incognito') });
Here is the suggested answer written in ES6 syntaxt and slightly cleand up.
const isIncognito = () => new Promise((resolve, reject) => {
const fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
reject('Cant determine whether browser is running in incognito mode!');
}
fs(window.TEMPORARY, 100, resolve.bind(null, false), resolve.bind(null, true));
});
// Usage
isIncognito()
.then(console.log)
.catch(console.error)
Other answers seem to be no longer valid in recent chrome versions.
The idea is to find out storage estimates to determine if the tab is incognito or not. Storage size is less for incognito tabs.
Run this code in both normal and incognito window and note down the quota size.
const {quota} = await navigator.storage.estimate();
console.log(quota);
use quota size difference to implement the logic for incognito mode detection.
below logic works for Chrome v105:
const { quota } = await navigator.storage.estimate();
if (quota.toString().length === 10) {
console.log("gotcha: this is incognito tab"); //quota = 1102885027
} else {
console.log("this is a normal tab"); //quota = 296630877388
}
Also, look at this solution for much wider support (includes other browsers as well)
detectIncognito.ts
demo: https://detectincognito.com/
This works in May 2021: https://jsfiddle.net/2b1dk8oa/
The script has to be executed in a webpage, which is in an iframe.
try{
var ls = localStorage;
alert("You are not in Incognito Mode.");
}
catch(e) { alert("You are in Incognito Mode."); }
In my hydbrid app (Phonegap), I am trying to write to localStorage in a very standard way :
window.localStorage.setItem("proDB", JSON.stringify(data));
or
window.localStorage["proDB"] = JSON.stringify(data);
But it doesn't work on Safari on iPad 2 (iOS 7.1).
It doesn't work and the whole app stops.
Here's the userAgent of this ipad :
Can you help me ?
Thanks
Please check whether you have Private Browsing enabled in Safari. In Safari Private Browsing mode, you get a quota of zero. Hence, all calls to localStorage.setItem will throw a quota exceeded error. Personally I think this is a huge mistake by Safari (as so many sites break), but it is what it is so we have to find a way around it. We can do this by:
Detecting whether we have a functional localStorage
Falling back to some replacement if not.
Read on if you want the details :)
1: Detecting a functional local storage
I am currently using this code to detect whether local storage is available, and fall back to a shim if not:
var DB;
try {
var x = '_localstorage_test_' + Date.now();
localStorage.setItem(x, x);
var y = localStorage.getItem(x);
localStorage.removeItem(x);
if (x !== y) {throw new Error();} // check we get back what we stored
DB = localStorage; // all fine
}
catch(e) {
// no localstorage available, use shim
DB = new MemoryStorage('my-app');
}
EDIT: Since writing this I have packaged up the feature detecting code. If you are using NPM you can install storage-available like so:
npm install --save storage-available
then you can use it in your code like this:
if (require('storage-available')('localStorage')) {
// Yay!
}
else {
// Awwww.....
}
2. Fall back to a shim
The easiest way to deal with the issue once we have detected the problem is to fall back to some other object that does not throw errors on every write.
memorystorage is a little library I wrote that follows the Web Storage API but just stores everything in memory. Because it uses the same API, you can use it as a drop-in replacement for localStorage and everything will function fine (though no data will survive page reload). It's Open Source so use as you please.
Background info
For more information on MemoryStorage and this issue in general, read my blog post on this topic: Introducing MemoryStorage.
I have set local storage key values through below logic using swift2.2
let jsStaring = "localStorage.setItem('Key', 'value')"
self.webView.stringByEvaluatingJavaScriptFromString(jsStaring)
Your first setItem example is correct. I don't believe that you can do the second option (localStorage["someKey"] = "someValue") though. Stick with the first one.
You mention hybrid - is it a PhoneGap or some other framework? Where in the app are you calling localStorage.setItem? If PhoneGap, be sure that everything has loaded via onDeviceReady first before trying to access localStorage:
<script type="text/javascript">
// Wait for PhoneGap to load
document.addEventListener("deviceready", onDeviceReady, false);
// PhoneGap is ready
function onDeviceReady() {
window.localStorage.setItem("key", "value");
}
</script>
Also, if the app freezes/stops working, in my experience it's because somewhere in the code you are accessing an object that is undefined. Perhaps try some debugging by checking if localStorage is undefined and logging it? Are you 100% sure that the "setItem" line is where it fails? Console.log is your friend, prove it! :)
if (localStorage === undefined) {
console.log("oops, localStorage not initialized yet.");
}
else {
window.localStorage.setItem("proDB", JSON.stringify(data));
console.log("localStorage available.");
}
I'm building an extranet for a company paranoid about security. They want to make sure that (among other things) their users are browsing the site with the Private Browsing mode switched on in their web browser so that no cookies or history is kept.
I found only this
http://jeremiahgrossman.blogspot.com/2009/03/detecting-private-browsing-mode.html
and
https://serverfault.com/questions/18966/force-safari-to-operate-in-private-mode-and-detect-that-state-from-a-webserver
The ideal solution would use no or minimal javascript. Would attempting to set a unique cookie work for all browsers and platforms? Anyone done this before?
thanks!
update
http://crypto.stanford.edu/~collinj/research/incognito/ uses the CSS visited technique of the browser fingerprinters mentioned by other posters- thanks for the hints.
I like it because it is small and elegant, but still want to be able to do it without javascript if possible.
Update June 2019
Google is removing the ability to detect Private Browsing Mode permanently in Chrome 76 onwards. So, if you're wanting to detect private browsing it's now impossible (unless you find a way to do it that Google hasn't found). The ability to detect private browsing mode has been acknowledged as a bug and was never intended.
To anyone else coming across this question, please note as of 2014, there is no reliable or accurate way to detect if someone is browsing in an incognito/private/safe browsing mode through Javascript or CSS. Previous solutions that once worked like the CSS history hack have since been rendered unusable by all browser vendors.
There should never be a situation where needing to detect private browsing mode on a normal day-to-day website is ever needed. People are choosing to browse anonymously and or not anonymously for their own reasons.
Browsers like Chrome and Firefox do not disable functionality like localStorage any more. They simply namespace it in a temporary location to prevent websites that use it from erroring out. Once you're finished browsing, the namespace is erased and nothing is saved. If you are testing for localStorage support regardless of mode, it will always return true for browsers that support it.
Other means of detecting private mode in Chrome specifically have been completely patched and will no longer work.
If it is required internally by a company, you should develop a browser plugin. Chrome and Firefox, in particular, expose internal API's which allow plugins to check if the user is in private browsing/incognito mode and action accordingly. It cannot be done outside of a plugin.
Here's an easier way to do detect privacy mode. This works in Safari only. I created it because a web app I am developing uses localStorage. LocalStorage is not available in Safari when in privacy mode, thus my app will not work.
On page load, run the script below. It shows an alert box if we cannot use localStorage.
try {
// try to use localStorage
localStorage.test = 2;
} catch (e) {
// there was an error so...
alert('You are in Privacy Mode\nPlease deactivate Privacy Mode and then reload the page.');
}
Current state
Google Chrome has developed further and leaves no more space for detection when using incognito mode. Same might apply for other browsers.
Old solutions (might partially work)
It is possible to detect enabled private browsing modes for the majority of used browsers. This includes Safari, Firefox, IE10, Edge and Google Chrome.
Firefox
When the private browsing mode of Firefox is enabled, the IndexedDB throws an InvalidStateError because it is not available in private browsing mode.
To very if that:
var db = indexedDB.open("test");
db.onerror = function(){/*Firefox PB enabled*/};
db.onsuccess =function(){/*Not enabled*/};
Safari
For Safari, the key is the local storage service. It is disabled in privacy mode. So try to access it and use a try-catch clause.
The following method works on both, OSX and iOS devices. Credits for this method are going to this question and answer
var storage = window.sessionStorage;
try {
storage.setItem("someKeyHere", "test");
storage.removeItem("someKeyHere");
} catch (e) {
if (e.code === DOMException.QUOTA_EXCEEDED_ERR && storage.length === 0) {
//Private here
}
}
IE10/Edge
Internet Explore is even going to disable the IndexedDB when in privacy mode. So check for existence. But that's not sufficient enough, because older browsers maybe don't even have an IDB. So do another check, e.g. for events that only IE10 and subsequent browser have/trigger. A related question on CodeReview can be found here
if(!window.indexedDB && (window.PointerEvent || window.MSPointerEvent)){
//Privacy Mode
}
Chrome
Update: This doesn't work since Chrome 76 (thanks to #jLynx)
Chromes Incognito mode can be verified by the file system. A great explanation can be found here on SO
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
console.log("FS check failed..");
return;
}
fs(window.TEMPORARY, 100, function (fs) {}, function (err) {
//Incognito mode
});
update on safari
Safari (release v29) patched localstorage so that you can no longer use it for detection.
I saw one other solution that tries to save blob/files in indexedDB and that will reject with an error saying that it can't store fils when surfing in private mode.
but there is a new / easier method with the new whatwg/fs
navigator.storage.getDirectory().then(root => {
console.log('not private')
}, err => {
console.log('private')
})
Chrome 83 arrives with redesigned security settings, third-party cookies blocked in Incognito by default!
So this one is easy, create a iframe to a third party site, have it send a postMessage back notifying you if navigator.cookieEnabled is true or false. Ofc users have the option to disable third party cookie as well. So i tested and disabled 3th party cookies in the settings. But it still said cookie was enabled on third-party iframes using navigator.cookieEnabled. it only became disabled once i used Incognito - perhaps a bug?
new Promise((rs, rj, m = new MessageChannel(), d = document, i = d.createElement('iframe')) => {
i.src = 'https://httpbin.org/base64/PHNjcmlwdD5vbm1lc3NhZ2UgPSBlID0+IGUuZGF0YS5wb3N0TWVzc2FnZShuYXZpZ2F0b3IuY29va2llRW5hYmxlZCk8L3NjcmlwdD4='
i.onload = _ => i.contentWindow.postMessage(m.port1, '*', [m.port1], m.port2.onmessage = e => i.remove(rs(e.data)))
i.hidden = 1
d.body.append(i)
}).then(thirdPartyCookieEabled =>
console.log('Third party cookie enabled:', thirdPartyCookieEabled)
)
You could also probably do it using only js + ajax but didn't want to set up a 2 servers to test it myself. but for this SameSite=none have to be set as well.
res = await fetch('https://httpbin.org/cookies/set?enabled=1', {
credentials: 'include'
})
json = await res.json()
console.log(!!json.cookies.enabled)
Here is my take on detecting private mode
function detectPrivateMode(cb) {
var db,
on = cb.bind(null, true),
off = cb.bind(null, false)
function tryls() {
try {
localStorage.length ? off() : (localStorage.x = 1, localStorage.removeItem("x"), off());
} catch (e) {
// Safari only enables cookie in private mode
// if cookie is disabled then all client side storage is disabled
// if all client side storage is disabled, then there is no point
// in using private mode
navigator.cookieEnabled ? on() : off();
}
}
// Blink (chrome & opera)
window.webkitRequestFileSystem ? webkitRequestFileSystem(0, 0, off, on)
// FF
: "MozAppearance" in document.documentElement.style ? (db = indexedDB.open("test"), db.onerror = on, db.onsuccess = off)
// Safari
: /constructor/i.test(window.HTMLElement) || window.safari ? tryls()
// IE10+ & edge
: !window.indexedDB && (window.PointerEvent || window.MSPointerEvent) ? on()
// Rest
: off()
}
detectPrivateMode(function (isPrivateMode) {
console.log('is private mode: ' + isPrivateMode)
})
edit found a modern, faster, synkronas way to try it in firefox (they don't have service workers in privat mode) similar to ie don't include indexedDB but the test only works in secure sites
: "MozAppearance" in document.documentElement.style ? navigator.serviceWorker ? off() : on()
There's no way for your web page to know, absolutely for sure, that the user is in private browsing mode. Any attempts to check for various browser features will need to change often as security implementations are updated. It may work for some time in some browsers, but not all.
If the company is that concerned about security, I'd suggest rolling your own Firefox or Chromium distribution with locked down privacy settings, and only allowing that custom client to connect to the extranet.
I have built a little library that will work on all major platforms and browsers that I have tested: https://github.com/jLynx/PrivateWindowCheck
You can simply call
isPrivateWindow(function(is_private) {
if(is_private)
alert('Private');
else
alert('Not Private');
});
Many seem to believe that detecting incognito/private mode is no longer possible, and I can assure you that's definitely not true. I have been unable to find any publicly available code that works on 100% of browsers, so I made my own.
As of October 2021 my code is able to detect private browsing modes on Safari 15 (macOS & iOS), Chrome 93, Edge 93, Firefox 93, Internet Explorer 10+, as well as Brave.
I should also note that detecting incognito has legitimate, non-paywall related uses, of which I am using as part of a larger private project. For example, fraudsters often use incognito when conducting credit card fraud. While alone someone filling in a checkout form in incognito doesn't immediately scream fraud, paired with other data points (such as the user using a VPN as well), I would be wary of accepting such an order.
Sites that block access based on whether one is in incognito suck, but I pin that down more to bad site design.
Shameless plug: https://github.com/Joe12387/detectIncognito
I also have a demo available: https://detectincognito.com/
I hope this is helpful.
The localStorage trick is a bug which has been fixed, and it doesn't work anymore in Safari 11.0.
There is an interesting alternative that works in Safari, Opera and Internet Explorer (not Chrome): those browser send a DNT: 1 header (Do Not Track).
It's not 100% reliable because this header can be enabled for normal browsing (it's disabled by default), but it can help to identify privacy-conscious users.
Web browsers behave differently when privacy mode is activated.
On many browsers the caching of resources is limited. It is possible to detect where a browser has been based on their CSS cache. Its possible to conduct this this attack without JavaScript.
The EFF is working on a project to fingerprint browsers. Parts of the browsers fingerprint will be different when privacy mode is activated. Go ahead, try it.
You're not going to block them if they don't have private browsing enabled.
Why have a smart message box at all?
Would attempting to set a unique cookie work for all browsers and platforms? Anyone done this before?
I think the most elegant solution would be to:
Perform a security leak test
If security leak test reveals issue
Tell user to check settings
Suggest privacy mode
Because as you said, not everyone can or needs to enable privacy mode.
I agree with DigitalSeas's sentiment that we should generally not try to detect if the user is in a "private browsing" mode. However, I recently discovered that FireFox now subscribes to a service called "disconnect.me", which provides the url blacklist they use in their "tracking protection" feature. Since disconnect.me blacklists certain social networks (e.g. Facebook's facebook.net), we found that their SDKs would not load in FireFox. Therefore, it seems reasonable that we could try and detect private browsing mode in order to provide a more useful and precise error message to our users.
With that justification out of the way, this gist claims to provide detection for private browsing in major browsers using tricks specific to those browsers. At the time of this writing (the gist may have been updated by the time you read this) the detection logic is as follows:
function retry(isDone, next) {
var current_trial = 0, max_retry = 50, interval = 10, is_timeout = false;
var id = window.setInterval(
function() {
if (isDone()) {
window.clearInterval(id);
next(is_timeout);
}
if (current_trial++ > max_retry) {
window.clearInterval(id);
is_timeout = true;
next(is_timeout);
}
},
10
);
}
function isIE10OrLater(user_agent) {
var ua = user_agent.toLowerCase();
if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
return false;
}
var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
if (match && parseInt(match[1], 10) >= 10) {
return true;
}
return false;
}
function detectPrivateMode(callback) {
var is_private;
if (window.webkitRequestFileSystem) {
window.webkitRequestFileSystem(
window.TEMPORARY, 1,
function() {
is_private = false;
},
function(e) {
console.log(e);
is_private = true;
}
);
} else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
var db;
try {
db = window.indexedDB.open('test');
} catch(e) {
is_private = true;
}
if (typeof is_private === 'undefined') {
retry(
function isDone() {
return db.readyState === 'done' ? true : false;
},
function next(is_timeout) {
if (!is_timeout) {
is_private = db.result ? false : true;
}
}
);
}
} else if (isIE10OrLater(window.navigator.userAgent)) {
is_private = false;
try {
if (!window.indexedDB) {
is_private = true;
}
} catch (e) {
is_private = true;
}
} else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
try {
window.localStorage.setItem('test', 1);
} catch(e) {
is_private = true;
}
if (typeof is_private === 'undefined') {
is_private = false;
window.localStorage.removeItem('test');
}
}
retry(
function isDone() {
return typeof is_private !== 'undefined' ? true : false;
},
function next(is_timeout) {
callback(is_private);
}
);
}
Browser makers are actively patching browser bugs that let sites identify private mode. Some bugs remain, but code to detect them is unlikely to be worth writing as the plan is it won't work for long.
The reason why browser makers are fixing these bugs is that sites like news publishers are using the bugs to identify private-mode users and refuse to serve them. The news publishers who want to do this tend to be using "metered paywalls", which let each browser view a certain number of free articles per day (or per week or whatever) and then start charging: since private-mode effectively resets the meter, the publishers don't want you to use private mode. But restricting private mode also restricts people who want to use it for other reasons, e.g. a domestic-abuse victim not wanting to keep history on a browser shared with their abuser, or someone not wanting their current activity to affect what advertisements they see in future, or some other reason. Although news sites that restrict private mode do tend to permit it if you log in, it's rather obvious that logging in lets them track you, so if you're trying to avoid being tracked then logging-in doesn't seem like an acceptable solution, hence browser makers want publishers not to be able to detect private mode.
There is another way of not keeping history: use a browser that lets you selectively clear it afterwards. Chrome for example can clear cookies, cache and history associated with specific domains, or for a specified period of time such as "for the last hour". That way you don't need private or incognito mode: you can get the same effect by browsing in normal mode and then erasing the traces. But you have to know how to do it, plus you must remember to do it, so it's a more troublesome approach than is using private mode. Still, I expect some people are using this as a workaround if their browser still has a bug that lets sites detect when they're in private mode and deny them service.
If you as a website author have a reason for not wanting your website to be stored in cache or history, for example if the site is meant to help domestic-abuse victims, then "how to detect private mode" might be the wrong question, as what you really want is "don't store this stuff in history, whether private mode is in use or not". Chris Coyier on css-tricks.com noted that window.location.replace() does not create a history item and techniques like this could be used to construct a site that at least doesn't leave its individual pages in history (at least not when browsed with Javascript switched on), but the referring page—possibly a dangerous-to-have search-result page—would still be in history, as would the final page they were on if they don't properly "sign out" of the site. (Intermediate pages might also be discernible via cache forensics, but anyone who can do that can probably also record the traffic as it happens, so we just have to hope the abuser is not that clever.) Another possible approach might be to create an innocent-looking cookie that stores the timestamp of the page's first load, and replace its content with something else if the current time exceeds that timestamp by too long, although its URL may still be loadable on another device or by clearing cookies (unless it was also session-dependent). But getting rid of the referring page is still going to be an issue, and as it's not always possible to confirm the user is in private mode, the best policy is probably to take steps to educate them about private mode anyway.
If you check their User-Agent, you can show them what both private mode and non private mode look like on their browser and OS, ask them to confirm which of the two matches their current window right now, and caution them if they choose the non private mode appearance. This won't work for browser and OS combinations you didn't test (but then neither will bug-based detection), and it assumes the user cares about their security enough to take the question seriously—but if they don't, the battle is probably already lost anyway.
Well, you wouldn't really distinguish private mode from "block all cookies" in that way, but apart from that rare situation I guess it should work.
The big problem IMO, is that this is a very very bad site design, not better than the good ol' "you need browser xxx to see this website" that was common in the '90s. Not all browser have a Private Browsing mode (as much as I despise IE, your cutting out IE7 users for instance) and those users won't be able to access your site at all.
Also, when I'm on the Internet I often have several tabs open with multiple website. It would be really annoying for me to have to switch to private mode just to see that website and not being able to access the other sites at the same time.
One thing you could do would be designing the site using sessions instead of cookies, so they won't be stored (as you don't use them...). And as for the history... really, what's the problem with that?
I've solved this issue by using two HTML pages. The main page define a status variable and set a cookie. The second page is opened in a new window (not tab), read the cookie and set status to the cookie value. Under MSIE, the cookie value is passed to the child page when the main page in normal mode. When in InPrivate Browsing mode, the cookie value is not passed to the child page (but is passed if you open a new tab).
The main.html page:
<script>
var myCookie="nocookie";
document.cookie="checkInPrivate=1";
var h=window.open("child.html", "_blank", "left=9999,height=200,width=200");
setTimeout(function() {
var status=null;
if (myCookie=="nocookie") {
status="unable to determine if we are InPrivate Browsing mode (child page did not set the cookie)";
} else if (myCookie.indexOf("checkInPrivate")>=0) {
status="not in InPrivate Browsing mode (child page did set the cookie)";
} else {
status="in InPrivate Browsing mode (child page set the cookie value but it was not provided)";
}
alert(status);
}, 200);
</script>
The child.html page:
Detecting MSIE's InPrivate Browsing mode...
<script>
window.opener.myCookie=document.cookie;
window.close();
</script>
I'm using InPrivate Browsing mode in order to prevent Browser Helper Objects (BHO) and browser extensions to be enabled, since BHO are most often malwares which can modify the web pages even if HTTPS and strong authentication are used. Internet Explorer 9 has a "Disable toolbars and extensions when InPrivate Browsing starts" in its "Privacy" settings.
However, this is not the ultimate way to prevent malicious browser extension: a malicious extension may change the main page behavior to make it think that the myCookie value has not been set and. We would wrongly assume that we are in InPrivate Browsing mode.
Note that I need cookies for my application so I do not use InPrivate Browsing for that purpose.
Write code to achieve following
1) In firefox test browser version. This method works with version >= 33.0 ( supports service workers ). Cannot use this method with old ( < 33.0 ) versions.
2) Try to set service worker.
3) If you can set,use or access a service worker you are 1000% not in private browsing mode as service workers cannot be interacted with on Firefox private browsing mode. I wish that they could be.
Quote:
"In Firefox, Service Worker APIs are hidden and cannot be used when the user is in private browsing mode"
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers
Not sure if its cause this question is old but Firefox does provide documentation on how to detect private browsing mode. However it involves using an import of theirs DXR PrivateBrowsingUtils:
try {
// Firefox 20+
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
...
}
} catch(e) {
// pre Firefox 20 (if you do not have access to a doc.
// might use doc.hasAttribute("privatebrowsingmode") then instead)
try {
var inPrivateBrowsing = Components.classes["#mozilla.org/privatebrowsing;1"].
getService(Components.interfaces.nsIPrivateBrowsingService).
privateBrowsingEnabled;
if (!inPrivateBrowsing) {
...
}
} catch(e) {
Components.utils.reportError(e);
return;
}
}
While creating my Safari extension, I found out that it was possible to query the boolean safari.self.browserWindow.activeTab.private. Below worked for me to check whether the browser was open in Private or not but only from the extension.
isPrivate = false;
try {
isPrivate = safari.self.browserWindow.activeTab.private;
} catch (_) {
isPrivate = true;
}
if (isPrivate === true){
console.log("Private window.");}
else {
console.log("Not private window.");}
Source: developer.apple.com | Instance Property private
function isPrivate(callback) {
callback || (callback = function(){});
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if(fs){
return fs(window.TEMPORARY, 1, callback.bind(this, false), callback.bind(this, true));
}
if(window.indexedDB && /Firefox/.test(window.navigator.userAgent)){
try {
var db = window.indexedDB.open('test');
var tryes = 0;
var interval = limit = 10;
var wait = function(check){
if(tryes >= limit){ return callback(true); } // Give up
return window.setTimeout(check, ++tryes * interval);
}
var evaluate = function(){
return db.readyState === 'done' ? callback(!db.result) : wait(evaluate);
}
return wait(evaluate);
} catch (e) {
return callback(true);
}
}
if (!!window.navigator.userAgent.match(/(MSIE|Trident|Edge)/)){
try {
return callback(!window.indexedDB);
} catch (e) {
return callback(true);
}
}
try {
window.openDatabase(null, null, null, null);
return callback(false);
} catch (e) {
return callback(true);
}
}
isPrivate( function(isPrivate) {
console.log('Private mode ===>', isPrivate);
});
You can find this gist to detect private browsing
var hasStorage = function() {
var mod,
result;
try {
mod = new Date;
localStorage.setItem(mod, mod.toString());
result = localStorage.getItem(mod) === mod.toString();
localStorage.removeItem(mod);
return result;
} catch (_error) {
return false;
}
},
hasCookies = function() {
var cookieEnabled = navigator.cookieEnabled ? true : false;
if (typeof navigator.cookieEnabled == 'undefined' && !cookieEnabled) {
document.cookie = 'testcookie';
cookieEnabled = (document.cookie.indexOf('testcookie') != -1) ? true : false;
}
return cookieEnabled;
};
if (!hasStorage()) {
document.getElementById('apple_storage').style.display = 'block';
} else if (!hasCookies()) {
document.getElementById('no_cookie').style.display = 'block';
}
<!-- ERROR FOR BLOCKED LOCAL STORAGE -->
<div id="apple_storage" style="display:none;">
<div class="modal-new alerts">
<h2>Attenion</h2>
Close
<div class="body">
<p>
Dear customer, to ensure proper use of the site it is necessary to abandon the
private browsing.
</p>
</div><!-- /.body -->
</div>
</div>
<!-- ERROR FOR BLOCKED COOKIES -->
<div id="no_cookie" style="display:none;">
<div class="modal-new alerts">
<h2>Attenion</h2>
Close
<div class="body">
<p>
Dear customer, to ensure proper use of the site it is necessary to enable cookies.
</p>
</div><!-- /.body -->
</div>
</div>
Is it possible to determine if Google Chrome is in incognito mode via a script?
Edit: I actually meant is it possible via user-script, but the answers assume JavaScript is running on a web page. I've re-asked the question here in regards to user scripts.
The functionality of this answer is Chrome version dependant. The most recent comment was this works in v90
Yes. The FileSystem API is disabled in incognito mode. Check out https://jsfiddle.net/w49x9f1a/ when you are and aren't in incognito mode.
Sample code:
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
console.log("check failed?");
} else {
fs(window.TEMPORARY,
100,
console.log.bind(console, "not in incognito mode"),
console.log.bind(console, "incognito mode"));
}
In Chrome 74 to 84.0.4147.135 you can determine this by estimating the available file system storage space
See the jsfiddle
if ('storage' in navigator && 'estimate' in navigator.storage) {
const {usage, quota} = await navigator.storage.estimate();
console.log(`Using ${usage} out of ${quota} bytes.`);
if(quota < 120000000){
console.log('Incognito')
} else {
console.log('Not Incognito')
}
} else {
console.log('Can not detect')
}
One way is to visit a unique URL and then check to see whether a link to that URL is treated as visited by CSS.
You can see an example of this in "Detecting Incognito" (Dead link).
Research paper by same author to replace Detecting Incognito link above
In main.html add an iframe,
<iframe id='testFrame' name='testFrame' onload='setUniqueSource(this)' src='' style="width:0; height:0; visibility:hidden;"></iframe>
, and some JavaScript code:
function checkResult() {
var a = frames[0].document.getElementById('test');
if (!a) return;
var color;
if (a.currentStyle) {
color = a.currentStyle.color;
} else {
color = frames[0].getComputedStyle(a, '').color;
}
var visited = (color == 'rgb(51, 102, 160)' || color == '#3366a0');
alert('mode is ' + (visited ? 'NOT Private' : 'Private'));
}
function setUniqueSource(frame) {
frame.src = "test.html?" + Math.random();
frame.onload = '';
}
Then in test.html that are loaded into the iFrame:
<style>
a:link { color: #336699; }
a:visited { color: #3366A0; }
</style>
<script>
setTimeout(function() {
var a = document.createElement('a');
a.href = location;
a.id = 'test';
document.body.appendChild(a);
parent.checkResult();
}, 100);
</script>
NOTE: trying this from the filesystem can make Chrome cry about "Unsafe Javascript". It
will, however, work serving from a webserver.
You can, in JavaScript, see JHurrah's answer. Except for not highlighting links, all incognito mode does is not save browse history and cookies. From google help page:
Webpages that you open and files downloaded while you are incognito
aren't recorded in your browsing and
download histories.
All new cookies are deleted after you close all incognito windows
that you've opened.
As you can see the differences between normal browsing and incognito happen after you visit the webpage, hence there is nothing that browser communicates to the server when it's in this mode.
You can see what exactly your browser sends to the server using one of many HTTP request analysers, like this one here. Compare the headers between normal session and incognito and you will see no difference.
If you are developing an Extension then you can use the tabs API to determine if a window/tab incognito.
More information can be found here.
If you are just working with a webpage, it is not easy, and it is designed to be that way. However, I have noticed that all attempts to open a database (window.database) fail when in incongnito, this is because when in incognito no trace of data is allowed to be left on the users machine.
I haven't tested it but I suspect all calls to localStorage fail too.
For those looking for a solution, here's a brief rundown of the current methods of detecting Private Browsing modes in various browsers as of October 2021:
Chromium: Similar to Vinnie James's answer, call navigator.storage.estimate(), grab the quota property and compare it to performance.memory.jsHeapSizeLimit. If the quota property is less than jsHeapSizeLimit, it's incognito. If jsHeapSizeLimit is undefined, use 1073741824 (1 GiB).
Safari for macOS: Use safari.pushNotification.requestPermission on a non-existent push server & grab the error. If "gesture" does not appear in the error, it's in private mode.
Safari for iOS: Create an iframe & add an error event listener using contentWindow.applicationCache on the iframe. If the error trips, it's in private mode.
Firefox: navigator.serviceWorker will be undefined in a private window.
Internet Explorer: window.indexedDB will be undefined in InPrivate mode.
You can see an implementation of these methods in the detectIncognito script I have available on GitHub.
Update This seems to not be working anymore
This uses a promise to wait for the asynchronous code to set a flag, so we can use it synchronously afterward.
let isIncognito = await new Promise((resolve, reject)=>{
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) reject('Check incognito failed');
else fs(window.TEMPORARY, 100, ()=>resolve(false), ()=>resolve(true));
});
then we can do
if(isIncognito) alert('in incognito');
else alert('not in incognito');
Note, to use await you need to be inside an async function. If you're not, you can wrap all your code inside one to be able to
Quick function based on Alok's Answer (note: this is asynchronous)
Update - not working anymore
function ifIncognito(incog,func){
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) console.log("checking incognito failed");
else {
if(incog) fs(window.TEMPORARY, 100, ()=>{}, func);
else fs(window.TEMPORARY, 100, func, ()=>{});
}
}
usage:
ifIncognito(true, ()=>{ alert('in incognito') });
// or
ifIncognito(false, ()=>{ alert('not in incognito') });
Here is the suggested answer written in ES6 syntaxt and slightly cleand up.
const isIncognito = () => new Promise((resolve, reject) => {
const fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
reject('Cant determine whether browser is running in incognito mode!');
}
fs(window.TEMPORARY, 100, resolve.bind(null, false), resolve.bind(null, true));
});
// Usage
isIncognito()
.then(console.log)
.catch(console.error)
Other answers seem to be no longer valid in recent chrome versions.
The idea is to find out storage estimates to determine if the tab is incognito or not. Storage size is less for incognito tabs.
Run this code in both normal and incognito window and note down the quota size.
const {quota} = await navigator.storage.estimate();
console.log(quota);
use quota size difference to implement the logic for incognito mode detection.
below logic works for Chrome v105:
const { quota } = await navigator.storage.estimate();
if (quota.toString().length === 10) {
console.log("gotcha: this is incognito tab"); //quota = 1102885027
} else {
console.log("this is a normal tab"); //quota = 296630877388
}
Also, look at this solution for much wider support (includes other browsers as well)
detectIncognito.ts
demo: https://detectincognito.com/
This works in May 2021: https://jsfiddle.net/2b1dk8oa/
The script has to be executed in a webpage, which is in an iframe.
try{
var ls = localStorage;
alert("You are not in Incognito Mode.");
}
catch(e) { alert("You are in Incognito Mode."); }
I'm building an extranet for a company paranoid about security. They want to make sure that (among other things) their users are browsing the site with the Private Browsing mode switched on in their web browser so that no cookies or history is kept.
I found only this
http://jeremiahgrossman.blogspot.com/2009/03/detecting-private-browsing-mode.html
and
https://serverfault.com/questions/18966/force-safari-to-operate-in-private-mode-and-detect-that-state-from-a-webserver
The ideal solution would use no or minimal javascript. Would attempting to set a unique cookie work for all browsers and platforms? Anyone done this before?
thanks!
update
http://crypto.stanford.edu/~collinj/research/incognito/ uses the CSS visited technique of the browser fingerprinters mentioned by other posters- thanks for the hints.
I like it because it is small and elegant, but still want to be able to do it without javascript if possible.
Update June 2019
Google is removing the ability to detect Private Browsing Mode permanently in Chrome 76 onwards. So, if you're wanting to detect private browsing it's now impossible (unless you find a way to do it that Google hasn't found). The ability to detect private browsing mode has been acknowledged as a bug and was never intended.
To anyone else coming across this question, please note as of 2014, there is no reliable or accurate way to detect if someone is browsing in an incognito/private/safe browsing mode through Javascript or CSS. Previous solutions that once worked like the CSS history hack have since been rendered unusable by all browser vendors.
There should never be a situation where needing to detect private browsing mode on a normal day-to-day website is ever needed. People are choosing to browse anonymously and or not anonymously for their own reasons.
Browsers like Chrome and Firefox do not disable functionality like localStorage any more. They simply namespace it in a temporary location to prevent websites that use it from erroring out. Once you're finished browsing, the namespace is erased and nothing is saved. If you are testing for localStorage support regardless of mode, it will always return true for browsers that support it.
Other means of detecting private mode in Chrome specifically have been completely patched and will no longer work.
If it is required internally by a company, you should develop a browser plugin. Chrome and Firefox, in particular, expose internal API's which allow plugins to check if the user is in private browsing/incognito mode and action accordingly. It cannot be done outside of a plugin.
Here's an easier way to do detect privacy mode. This works in Safari only. I created it because a web app I am developing uses localStorage. LocalStorage is not available in Safari when in privacy mode, thus my app will not work.
On page load, run the script below. It shows an alert box if we cannot use localStorage.
try {
// try to use localStorage
localStorage.test = 2;
} catch (e) {
// there was an error so...
alert('You are in Privacy Mode\nPlease deactivate Privacy Mode and then reload the page.');
}
Current state
Google Chrome has developed further and leaves no more space for detection when using incognito mode. Same might apply for other browsers.
Old solutions (might partially work)
It is possible to detect enabled private browsing modes for the majority of used browsers. This includes Safari, Firefox, IE10, Edge and Google Chrome.
Firefox
When the private browsing mode of Firefox is enabled, the IndexedDB throws an InvalidStateError because it is not available in private browsing mode.
To very if that:
var db = indexedDB.open("test");
db.onerror = function(){/*Firefox PB enabled*/};
db.onsuccess =function(){/*Not enabled*/};
Safari
For Safari, the key is the local storage service. It is disabled in privacy mode. So try to access it and use a try-catch clause.
The following method works on both, OSX and iOS devices. Credits for this method are going to this question and answer
var storage = window.sessionStorage;
try {
storage.setItem("someKeyHere", "test");
storage.removeItem("someKeyHere");
} catch (e) {
if (e.code === DOMException.QUOTA_EXCEEDED_ERR && storage.length === 0) {
//Private here
}
}
IE10/Edge
Internet Explore is even going to disable the IndexedDB when in privacy mode. So check for existence. But that's not sufficient enough, because older browsers maybe don't even have an IDB. So do another check, e.g. for events that only IE10 and subsequent browser have/trigger. A related question on CodeReview can be found here
if(!window.indexedDB && (window.PointerEvent || window.MSPointerEvent)){
//Privacy Mode
}
Chrome
Update: This doesn't work since Chrome 76 (thanks to #jLynx)
Chromes Incognito mode can be verified by the file system. A great explanation can be found here on SO
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if (!fs) {
console.log("FS check failed..");
return;
}
fs(window.TEMPORARY, 100, function (fs) {}, function (err) {
//Incognito mode
});
update on safari
Safari (release v29) patched localstorage so that you can no longer use it for detection.
I saw one other solution that tries to save blob/files in indexedDB and that will reject with an error saying that it can't store fils when surfing in private mode.
but there is a new / easier method with the new whatwg/fs
navigator.storage.getDirectory().then(root => {
console.log('not private')
}, err => {
console.log('private')
})
Chrome 83 arrives with redesigned security settings, third-party cookies blocked in Incognito by default!
So this one is easy, create a iframe to a third party site, have it send a postMessage back notifying you if navigator.cookieEnabled is true or false. Ofc users have the option to disable third party cookie as well. So i tested and disabled 3th party cookies in the settings. But it still said cookie was enabled on third-party iframes using navigator.cookieEnabled. it only became disabled once i used Incognito - perhaps a bug?
new Promise((rs, rj, m = new MessageChannel(), d = document, i = d.createElement('iframe')) => {
i.src = 'https://httpbin.org/base64/PHNjcmlwdD5vbm1lc3NhZ2UgPSBlID0+IGUuZGF0YS5wb3N0TWVzc2FnZShuYXZpZ2F0b3IuY29va2llRW5hYmxlZCk8L3NjcmlwdD4='
i.onload = _ => i.contentWindow.postMessage(m.port1, '*', [m.port1], m.port2.onmessage = e => i.remove(rs(e.data)))
i.hidden = 1
d.body.append(i)
}).then(thirdPartyCookieEabled =>
console.log('Third party cookie enabled:', thirdPartyCookieEabled)
)
You could also probably do it using only js + ajax but didn't want to set up a 2 servers to test it myself. but for this SameSite=none have to be set as well.
res = await fetch('https://httpbin.org/cookies/set?enabled=1', {
credentials: 'include'
})
json = await res.json()
console.log(!!json.cookies.enabled)
Here is my take on detecting private mode
function detectPrivateMode(cb) {
var db,
on = cb.bind(null, true),
off = cb.bind(null, false)
function tryls() {
try {
localStorage.length ? off() : (localStorage.x = 1, localStorage.removeItem("x"), off());
} catch (e) {
// Safari only enables cookie in private mode
// if cookie is disabled then all client side storage is disabled
// if all client side storage is disabled, then there is no point
// in using private mode
navigator.cookieEnabled ? on() : off();
}
}
// Blink (chrome & opera)
window.webkitRequestFileSystem ? webkitRequestFileSystem(0, 0, off, on)
// FF
: "MozAppearance" in document.documentElement.style ? (db = indexedDB.open("test"), db.onerror = on, db.onsuccess = off)
// Safari
: /constructor/i.test(window.HTMLElement) || window.safari ? tryls()
// IE10+ & edge
: !window.indexedDB && (window.PointerEvent || window.MSPointerEvent) ? on()
// Rest
: off()
}
detectPrivateMode(function (isPrivateMode) {
console.log('is private mode: ' + isPrivateMode)
})
edit found a modern, faster, synkronas way to try it in firefox (they don't have service workers in privat mode) similar to ie don't include indexedDB but the test only works in secure sites
: "MozAppearance" in document.documentElement.style ? navigator.serviceWorker ? off() : on()
There's no way for your web page to know, absolutely for sure, that the user is in private browsing mode. Any attempts to check for various browser features will need to change often as security implementations are updated. It may work for some time in some browsers, but not all.
If the company is that concerned about security, I'd suggest rolling your own Firefox or Chromium distribution with locked down privacy settings, and only allowing that custom client to connect to the extranet.
I have built a little library that will work on all major platforms and browsers that I have tested: https://github.com/jLynx/PrivateWindowCheck
You can simply call
isPrivateWindow(function(is_private) {
if(is_private)
alert('Private');
else
alert('Not Private');
});
Many seem to believe that detecting incognito/private mode is no longer possible, and I can assure you that's definitely not true. I have been unable to find any publicly available code that works on 100% of browsers, so I made my own.
As of October 2021 my code is able to detect private browsing modes on Safari 15 (macOS & iOS), Chrome 93, Edge 93, Firefox 93, Internet Explorer 10+, as well as Brave.
I should also note that detecting incognito has legitimate, non-paywall related uses, of which I am using as part of a larger private project. For example, fraudsters often use incognito when conducting credit card fraud. While alone someone filling in a checkout form in incognito doesn't immediately scream fraud, paired with other data points (such as the user using a VPN as well), I would be wary of accepting such an order.
Sites that block access based on whether one is in incognito suck, but I pin that down more to bad site design.
Shameless plug: https://github.com/Joe12387/detectIncognito
I also have a demo available: https://detectincognito.com/
I hope this is helpful.
The localStorage trick is a bug which has been fixed, and it doesn't work anymore in Safari 11.0.
There is an interesting alternative that works in Safari, Opera and Internet Explorer (not Chrome): those browser send a DNT: 1 header (Do Not Track).
It's not 100% reliable because this header can be enabled for normal browsing (it's disabled by default), but it can help to identify privacy-conscious users.
Web browsers behave differently when privacy mode is activated.
On many browsers the caching of resources is limited. It is possible to detect where a browser has been based on their CSS cache. Its possible to conduct this this attack without JavaScript.
The EFF is working on a project to fingerprint browsers. Parts of the browsers fingerprint will be different when privacy mode is activated. Go ahead, try it.
You're not going to block them if they don't have private browsing enabled.
Why have a smart message box at all?
Would attempting to set a unique cookie work for all browsers and platforms? Anyone done this before?
I think the most elegant solution would be to:
Perform a security leak test
If security leak test reveals issue
Tell user to check settings
Suggest privacy mode
Because as you said, not everyone can or needs to enable privacy mode.
I agree with DigitalSeas's sentiment that we should generally not try to detect if the user is in a "private browsing" mode. However, I recently discovered that FireFox now subscribes to a service called "disconnect.me", which provides the url blacklist they use in their "tracking protection" feature. Since disconnect.me blacklists certain social networks (e.g. Facebook's facebook.net), we found that their SDKs would not load in FireFox. Therefore, it seems reasonable that we could try and detect private browsing mode in order to provide a more useful and precise error message to our users.
With that justification out of the way, this gist claims to provide detection for private browsing in major browsers using tricks specific to those browsers. At the time of this writing (the gist may have been updated by the time you read this) the detection logic is as follows:
function retry(isDone, next) {
var current_trial = 0, max_retry = 50, interval = 10, is_timeout = false;
var id = window.setInterval(
function() {
if (isDone()) {
window.clearInterval(id);
next(is_timeout);
}
if (current_trial++ > max_retry) {
window.clearInterval(id);
is_timeout = true;
next(is_timeout);
}
},
10
);
}
function isIE10OrLater(user_agent) {
var ua = user_agent.toLowerCase();
if (ua.indexOf('msie') === 0 && ua.indexOf('trident') === 0) {
return false;
}
var match = /(?:msie|rv:)\s?([\d\.]+)/.exec(ua);
if (match && parseInt(match[1], 10) >= 10) {
return true;
}
return false;
}
function detectPrivateMode(callback) {
var is_private;
if (window.webkitRequestFileSystem) {
window.webkitRequestFileSystem(
window.TEMPORARY, 1,
function() {
is_private = false;
},
function(e) {
console.log(e);
is_private = true;
}
);
} else if (window.indexedDB && /Firefox/.test(window.navigator.userAgent)) {
var db;
try {
db = window.indexedDB.open('test');
} catch(e) {
is_private = true;
}
if (typeof is_private === 'undefined') {
retry(
function isDone() {
return db.readyState === 'done' ? true : false;
},
function next(is_timeout) {
if (!is_timeout) {
is_private = db.result ? false : true;
}
}
);
}
} else if (isIE10OrLater(window.navigator.userAgent)) {
is_private = false;
try {
if (!window.indexedDB) {
is_private = true;
}
} catch (e) {
is_private = true;
}
} else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
try {
window.localStorage.setItem('test', 1);
} catch(e) {
is_private = true;
}
if (typeof is_private === 'undefined') {
is_private = false;
window.localStorage.removeItem('test');
}
}
retry(
function isDone() {
return typeof is_private !== 'undefined' ? true : false;
},
function next(is_timeout) {
callback(is_private);
}
);
}
Browser makers are actively patching browser bugs that let sites identify private mode. Some bugs remain, but code to detect them is unlikely to be worth writing as the plan is it won't work for long.
The reason why browser makers are fixing these bugs is that sites like news publishers are using the bugs to identify private-mode users and refuse to serve them. The news publishers who want to do this tend to be using "metered paywalls", which let each browser view a certain number of free articles per day (or per week or whatever) and then start charging: since private-mode effectively resets the meter, the publishers don't want you to use private mode. But restricting private mode also restricts people who want to use it for other reasons, e.g. a domestic-abuse victim not wanting to keep history on a browser shared with their abuser, or someone not wanting their current activity to affect what advertisements they see in future, or some other reason. Although news sites that restrict private mode do tend to permit it if you log in, it's rather obvious that logging in lets them track you, so if you're trying to avoid being tracked then logging-in doesn't seem like an acceptable solution, hence browser makers want publishers not to be able to detect private mode.
There is another way of not keeping history: use a browser that lets you selectively clear it afterwards. Chrome for example can clear cookies, cache and history associated with specific domains, or for a specified period of time such as "for the last hour". That way you don't need private or incognito mode: you can get the same effect by browsing in normal mode and then erasing the traces. But you have to know how to do it, plus you must remember to do it, so it's a more troublesome approach than is using private mode. Still, I expect some people are using this as a workaround if their browser still has a bug that lets sites detect when they're in private mode and deny them service.
If you as a website author have a reason for not wanting your website to be stored in cache or history, for example if the site is meant to help domestic-abuse victims, then "how to detect private mode" might be the wrong question, as what you really want is "don't store this stuff in history, whether private mode is in use or not". Chris Coyier on css-tricks.com noted that window.location.replace() does not create a history item and techniques like this could be used to construct a site that at least doesn't leave its individual pages in history (at least not when browsed with Javascript switched on), but the referring page—possibly a dangerous-to-have search-result page—would still be in history, as would the final page they were on if they don't properly "sign out" of the site. (Intermediate pages might also be discernible via cache forensics, but anyone who can do that can probably also record the traffic as it happens, so we just have to hope the abuser is not that clever.) Another possible approach might be to create an innocent-looking cookie that stores the timestamp of the page's first load, and replace its content with something else if the current time exceeds that timestamp by too long, although its URL may still be loadable on another device or by clearing cookies (unless it was also session-dependent). But getting rid of the referring page is still going to be an issue, and as it's not always possible to confirm the user is in private mode, the best policy is probably to take steps to educate them about private mode anyway.
If you check their User-Agent, you can show them what both private mode and non private mode look like on their browser and OS, ask them to confirm which of the two matches their current window right now, and caution them if they choose the non private mode appearance. This won't work for browser and OS combinations you didn't test (but then neither will bug-based detection), and it assumes the user cares about their security enough to take the question seriously—but if they don't, the battle is probably already lost anyway.
Well, you wouldn't really distinguish private mode from "block all cookies" in that way, but apart from that rare situation I guess it should work.
The big problem IMO, is that this is a very very bad site design, not better than the good ol' "you need browser xxx to see this website" that was common in the '90s. Not all browser have a Private Browsing mode (as much as I despise IE, your cutting out IE7 users for instance) and those users won't be able to access your site at all.
Also, when I'm on the Internet I often have several tabs open with multiple website. It would be really annoying for me to have to switch to private mode just to see that website and not being able to access the other sites at the same time.
One thing you could do would be designing the site using sessions instead of cookies, so they won't be stored (as you don't use them...). And as for the history... really, what's the problem with that?
I've solved this issue by using two HTML pages. The main page define a status variable and set a cookie. The second page is opened in a new window (not tab), read the cookie and set status to the cookie value. Under MSIE, the cookie value is passed to the child page when the main page in normal mode. When in InPrivate Browsing mode, the cookie value is not passed to the child page (but is passed if you open a new tab).
The main.html page:
<script>
var myCookie="nocookie";
document.cookie="checkInPrivate=1";
var h=window.open("child.html", "_blank", "left=9999,height=200,width=200");
setTimeout(function() {
var status=null;
if (myCookie=="nocookie") {
status="unable to determine if we are InPrivate Browsing mode (child page did not set the cookie)";
} else if (myCookie.indexOf("checkInPrivate")>=0) {
status="not in InPrivate Browsing mode (child page did set the cookie)";
} else {
status="in InPrivate Browsing mode (child page set the cookie value but it was not provided)";
}
alert(status);
}, 200);
</script>
The child.html page:
Detecting MSIE's InPrivate Browsing mode...
<script>
window.opener.myCookie=document.cookie;
window.close();
</script>
I'm using InPrivate Browsing mode in order to prevent Browser Helper Objects (BHO) and browser extensions to be enabled, since BHO are most often malwares which can modify the web pages even if HTTPS and strong authentication are used. Internet Explorer 9 has a "Disable toolbars and extensions when InPrivate Browsing starts" in its "Privacy" settings.
However, this is not the ultimate way to prevent malicious browser extension: a malicious extension may change the main page behavior to make it think that the myCookie value has not been set and. We would wrongly assume that we are in InPrivate Browsing mode.
Note that I need cookies for my application so I do not use InPrivate Browsing for that purpose.
Write code to achieve following
1) In firefox test browser version. This method works with version >= 33.0 ( supports service workers ). Cannot use this method with old ( < 33.0 ) versions.
2) Try to set service worker.
3) If you can set,use or access a service worker you are 1000% not in private browsing mode as service workers cannot be interacted with on Firefox private browsing mode. I wish that they could be.
Quote:
"In Firefox, Service Worker APIs are hidden and cannot be used when the user is in private browsing mode"
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers
Not sure if its cause this question is old but Firefox does provide documentation on how to detect private browsing mode. However it involves using an import of theirs DXR PrivateBrowsingUtils:
try {
// Firefox 20+
Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
...
}
} catch(e) {
// pre Firefox 20 (if you do not have access to a doc.
// might use doc.hasAttribute("privatebrowsingmode") then instead)
try {
var inPrivateBrowsing = Components.classes["#mozilla.org/privatebrowsing;1"].
getService(Components.interfaces.nsIPrivateBrowsingService).
privateBrowsingEnabled;
if (!inPrivateBrowsing) {
...
}
} catch(e) {
Components.utils.reportError(e);
return;
}
}
While creating my Safari extension, I found out that it was possible to query the boolean safari.self.browserWindow.activeTab.private. Below worked for me to check whether the browser was open in Private or not but only from the extension.
isPrivate = false;
try {
isPrivate = safari.self.browserWindow.activeTab.private;
} catch (_) {
isPrivate = true;
}
if (isPrivate === true){
console.log("Private window.");}
else {
console.log("Not private window.");}
Source: developer.apple.com | Instance Property private
function isPrivate(callback) {
callback || (callback = function(){});
var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
if(fs){
return fs(window.TEMPORARY, 1, callback.bind(this, false), callback.bind(this, true));
}
if(window.indexedDB && /Firefox/.test(window.navigator.userAgent)){
try {
var db = window.indexedDB.open('test');
var tryes = 0;
var interval = limit = 10;
var wait = function(check){
if(tryes >= limit){ return callback(true); } // Give up
return window.setTimeout(check, ++tryes * interval);
}
var evaluate = function(){
return db.readyState === 'done' ? callback(!db.result) : wait(evaluate);
}
return wait(evaluate);
} catch (e) {
return callback(true);
}
}
if (!!window.navigator.userAgent.match(/(MSIE|Trident|Edge)/)){
try {
return callback(!window.indexedDB);
} catch (e) {
return callback(true);
}
}
try {
window.openDatabase(null, null, null, null);
return callback(false);
} catch (e) {
return callback(true);
}
}
isPrivate( function(isPrivate) {
console.log('Private mode ===>', isPrivate);
});
You can find this gist to detect private browsing
var hasStorage = function() {
var mod,
result;
try {
mod = new Date;
localStorage.setItem(mod, mod.toString());
result = localStorage.getItem(mod) === mod.toString();
localStorage.removeItem(mod);
return result;
} catch (_error) {
return false;
}
},
hasCookies = function() {
var cookieEnabled = navigator.cookieEnabled ? true : false;
if (typeof navigator.cookieEnabled == 'undefined' && !cookieEnabled) {
document.cookie = 'testcookie';
cookieEnabled = (document.cookie.indexOf('testcookie') != -1) ? true : false;
}
return cookieEnabled;
};
if (!hasStorage()) {
document.getElementById('apple_storage').style.display = 'block';
} else if (!hasCookies()) {
document.getElementById('no_cookie').style.display = 'block';
}
<!-- ERROR FOR BLOCKED LOCAL STORAGE -->
<div id="apple_storage" style="display:none;">
<div class="modal-new alerts">
<h2>Attenion</h2>
Close
<div class="body">
<p>
Dear customer, to ensure proper use of the site it is necessary to abandon the
private browsing.
</p>
</div><!-- /.body -->
</div>
</div>
<!-- ERROR FOR BLOCKED COOKIES -->
<div id="no_cookie" style="display:none;">
<div class="modal-new alerts">
<h2>Attenion</h2>
Close
<div class="body">
<p>
Dear customer, to ensure proper use of the site it is necessary to enable cookies.
</p>
</div><!-- /.body -->
</div>
</div>