Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Detect private browsing mode (InPrivate Browsing or Incognito).
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);
}
);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="result"></div>
<script src="detect-private-browsing.js"></script>
<script>
detectPrivateMode(
function(is_private) {
document.getElementById('result').innerHTML = typeof is_private === 'undefined' ? 'cannot detect' : is_private ? 'private' : 'not private';
}
);
</script>
</body>
</html>
@thelinuxlich

This comment has been minimized.

Show comment
Hide comment
@thelinuxlich

thelinuxlich Oct 21, 2014

Fantastic! It works!

Fantastic! It works!

@AndreSchwarzer

This comment has been minimized.

Show comment
Hide comment
@AndreSchwarzer

AndreSchwarzer Jan 21, 2015

Doesn't work in chrome :(

Doesn't work in chrome :(

@apastuhov

This comment has been minimized.

Show comment
Hide comment
@apastuhov

apastuhov Jun 1, 2015

I tried to test in Chrome and IE 11. Your script return "private" even when it is not private.

I tried to test in Chrome and IE 11. Your script return "private" even when it is not private.

@alexesDev

This comment has been minimized.

Show comment
Hide comment
@alexesDev

alexesDev Aug 25, 2015

Eslint check...

   2:41  error  interval is defined but never used    no-unused-vars
  40:15  error  e is defined but never used           no-unused-vars
  90:16  error  is_timeout is defined but never used  no-unused-vars

Eslint check...

   2:41  error  interval is defined but never used    no-unused-vars
  40:15  error  e is defined but never used           no-unused-vars
  90:16  error  is_timeout is defined but never used  no-unused-vars
@yukulele

This comment has been minimized.

Show comment
Hide comment
@yukulele

yukulele Jan 5, 2016

how it works?

yukulele commented Jan 5, 2016

how it works?

@chickenwing

This comment has been minimized.

Show comment
Hide comment
@chickenwing

chickenwing Feb 7, 2016

hmm, could this be expanded to work in the edge browser?

hmm, could this be expanded to work in the edge browser?

@sundip111

This comment has been minimized.

Show comment
Hide comment
@sundip111

sundip111 Apr 5, 2016

Awesome, works in all major browsers on both desktop and mobile.

Modify "isIE10OrLater" function as following for it to work with Edge browser:

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; } var edge = /edge/.exec(ua); if(edge && edge[0] == "edge"){ return true; } return false; }

Awesome, works in all major browsers on both desktop and mobile.

Modify "isIE10OrLater" function as following for it to work with Edge browser:

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; } var edge = /edge/.exec(ua); if(edge && edge[0] == "edge"){ return true; } return false; }

@jimmywarting

This comment has been minimized.

Show comment
Hide comment
@jimmywarting

jimmywarting May 5, 2016

Is userAgent sniffing good? how about:

var isSafari = /constructor/i.test(window.HTMLElement);
var isFF = 'MozAppearance' in document.documentElement.style;
var isIE10OrLater = document.body.style.msTouchAction !== undefined;

From: http://browserhacks.com
Btw, i ended up with 1/4 of that code using other methods: https://jsfiddle.net/Lj2fs5kr/
tested in latest Chrome, FF, Safari everything is good there. Don't have IE on Mac
I think you are overusing try/catch more then necessary...?

Also found something about InPrivateFilteringEnabled in IE, maybe that could be to any use?
Update: tried InPrivateFilteringEnabled in edge 13, didn't work

jimmywarting commented May 5, 2016

Is userAgent sniffing good? how about:

var isSafari = /constructor/i.test(window.HTMLElement);
var isFF = 'MozAppearance' in document.documentElement.style;
var isIE10OrLater = document.body.style.msTouchAction !== undefined;

From: http://browserhacks.com
Btw, i ended up with 1/4 of that code using other methods: https://jsfiddle.net/Lj2fs5kr/
tested in latest Chrome, FF, Safari everything is good there. Don't have IE on Mac
I think you are overusing try/catch more then necessary...?

Also found something about InPrivateFilteringEnabled in IE, maybe that could be to any use?
Update: tried InPrivateFilteringEnabled in edge 13, didn't work

@radyk

This comment has been minimized.

Show comment
Hide comment
@radyk

radyk Jun 1, 2016

https://jsfiddle.net/Lj2fs5kr/

How do I use this above to test at the appropriate point in my forum. I need something like this as a test but I have no idea how to actually use this code in the real world. I can determine where to test but not how to call this code.

radyk commented Jun 1, 2016

https://jsfiddle.net/Lj2fs5kr/

How do I use this above to test at the appropriate point in my forum. I need something like this as a test but I have no idea how to actually use this code in the real world. I can determine where to test but not how to call this code.

@slonoed

This comment has been minimized.

Show comment
Hide comment
@slonoed

slonoed Jun 30, 2016

This line in FF incognito raise InvalidStateError
https://gist.github.com/cou929/7973956#file-detect-private-browsing-js-L48
It looks like error happened in different thread. How catch it?

slonoed commented Jun 30, 2016

This line in FF incognito raise InvalidStateError
https://gist.github.com/cou929/7973956#file-detect-private-browsing-js-L48
It looks like error happened in different thread. How catch it?

@dev0x10

This comment has been minimized.

Show comment
Hide comment

dev0x10 commented Oct 3, 2016

@charltoons

This comment has been minimized.

Show comment
Hide comment
@charltoons

charltoons Jan 13, 2017

@slonoed You can catch the error by returning true in onerror. That one took me a few hours.

@slonoed You can catch the error by returning true in onerror. That one took me a few hours.

@kylestev

This comment has been minimized.

Show comment
Hide comment
@kylestev

kylestev Jan 14, 2017

@charltoons Glad I saw your comment! Thanks for saving me a few hours 😜

kylestev commented Jan 14, 2017

@charltoons Glad I saw your comment! Thanks for saving me a few hours 😜

@jonathanlermitage

This comment has been minimized.

Show comment
Hide comment
@jonathanlermitage

jonathanlermitage Feb 22, 2017

Doesn't work if you use uBlock Origin :)

Doesn't work if you use uBlock Origin :)

@bkstorm

This comment has been minimized.

Show comment
Hide comment
@bkstorm

bkstorm Mar 28, 2017

It works well with Chrome, Firefox and Safari. Good job 👍

bkstorm commented Mar 28, 2017

It works well with Chrome, Firefox and Safari. Good job 👍

@Vamshidharredy

This comment has been minimized.

Show comment
Hide comment
@Vamshidharredy

Vamshidharredy Mar 29, 2017

Broken for the latest Chrome. Chrome 57.

Broken for the latest Chrome. Chrome 57.

@Vadimgorlenko

This comment has been minimized.

Show comment
Hide comment
@Vadimgorlenko

Vadimgorlenko Apr 12, 2017

Works fine for me in latest Chrome and FF (Win 10), but does not work in Edge

Works fine for me in latest Chrome and FF (Win 10), but does not work in Edge

@lucasrodriguex

This comment has been minimized.

Show comment
Hide comment
@lucasrodriguex

lucasrodriguex Apr 24, 2017

Doesn't work with Chrome version 57+

Doesn't work with Chrome version 57+

@cesky84

This comment has been minimized.

Show comment
Hide comment
@cesky84

cesky84 Apr 27, 2017

Doesn't work with FF 53 on mac return only private result

cesky84 commented Apr 27, 2017

Doesn't work with FF 53 on mac return only private result

@rohankhudedev

This comment has been minimized.

Show comment
Hide comment
@rohankhudedev

rohankhudedev Jun 21, 2017

i did testing on some latest browser versions

Working-

Firefox - 54(latest), 53
Chrome - 58(latest)
Safari - 6.0.2, 5.1
IE - 11, 10
Opera - 45(latest)

Not working on -

UC browser - 11.3(latest)

rohankhudedev commented Jun 21, 2017

i did testing on some latest browser versions

Working-

Firefox - 54(latest), 53
Chrome - 58(latest)
Safari - 6.0.2, 5.1
IE - 11, 10
Opera - 45(latest)

Not working on -

UC browser - 11.3(latest)

@PunKeel

This comment has been minimized.

Show comment
Hide comment
@PunKeel

PunKeel Jul 8, 2017

Safari throws an error when cookies are disabled globally (private mode or not) because it also breaks the localStorage.
The script aborts, and callback is never called :-(

> window.localStorage; console.log("hello");
< SecurityError (DOM Exception 18): The operation is insecure.

The fix: use a try-catch to interact with the localStorage

Bonus (thanks to @jherax): use navigator.cookieEnabled to detect if cookies are enabled. cf https://gist.github.com/jherax/a81c8c132d09cc354a0e2cb911841ff1

My fix, which might not work in every cases (but works fine for me): check if cookies are enabled before checking if localStorage is available:

- } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
+ } else if (/Safari/.test(window.navigator.userAgent) && navigator.cookieEnabled && window.localStorage) {

PunKeel commented Jul 8, 2017

Safari throws an error when cookies are disabled globally (private mode or not) because it also breaks the localStorage.
The script aborts, and callback is never called :-(

> window.localStorage; console.log("hello");
< SecurityError (DOM Exception 18): The operation is insecure.

The fix: use a try-catch to interact with the localStorage

Bonus (thanks to @jherax): use navigator.cookieEnabled to detect if cookies are enabled. cf https://gist.github.com/jherax/a81c8c132d09cc354a0e2cb911841ff1

My fix, which might not work in every cases (but works fine for me): check if cookies are enabled before checking if localStorage is available:

- } else if (window.localStorage && /Safari/.test(window.navigator.userAgent)) {
+ } else if (/Safari/.test(window.navigator.userAgent) && navigator.cookieEnabled && window.localStorage) {
@BlueCutOfficial

This comment has been minimized.

Show comment
Hide comment
@BlueCutOfficial

BlueCutOfficial Jul 25, 2017

Microsoft Edge passes the case for Safari.
Add this at the top of Safari case block :

 if (!window.indexedDB && (window.PointerEvent || window.MSPointerEvent)) {
  is_private = true;
}

No problem for Firefox / Chrome / Safari, nice job ! :)

BlueCutOfficial commented Jul 25, 2017

Microsoft Edge passes the case for Safari.
Add this at the top of Safari case block :

 if (!window.indexedDB && (window.PointerEvent || window.MSPointerEvent)) {
  is_private = true;
}

No problem for Firefox / Chrome / Safari, nice job ! :)

@JessicaYeh

This comment has been minimized.

Show comment
Hide comment
@JessicaYeh

JessicaYeh Aug 16, 2017

On iOS 11 Safari (and probably the new version of desktop Safari too? but didn't check that one), that trick of seeing if window.localStorage.setItem('test', 1); throws an error no longer works, because it no longer throws an error, and it also properly sets the localStorage item. Has anyone figured out any other way to check for private browsing mode in the new versions of Safari?

On iOS 11 Safari (and probably the new version of desktop Safari too? but didn't check that one), that trick of seeing if window.localStorage.setItem('test', 1); throws an error no longer works, because it no longer throws an error, and it also properly sets the localStorage item. Has anyone figured out any other way to check for private browsing mode in the new versions of Safari?

@N1CHTN3BIH

This comment has been minimized.

Show comment
Hide comment
@N1CHTN3BIH

N1CHTN3BIH Sep 13, 2017

IE and Mozilla is returning private, but its not.

IE and Mozilla is returning private, but its not.

@Maykonn

This comment has been minimized.

Show comment
Hide comment
@Maykonn

Maykonn Sep 27, 2017

@JessicaYeh on desktop version 11 not works more too.

Maykonn commented Sep 27, 2017

@JessicaYeh on desktop version 11 not works more too.

@Maykonn

This comment has been minimized.

Show comment
Hide comment
@Maykonn

Maykonn Oct 6, 2017

On Firefox ESR 53.0 not working. The script always returns as private.

Maykonn commented Oct 6, 2017

On Firefox ESR 53.0 not working. The script always returns as private.

@Maykonn

This comment has been minimized.

Show comment
Hide comment
@Maykonn

Maykonn Oct 6, 2017

I asked a question on Stackoverflow with more details about Firefox specific errors, here.

Maykonn commented Oct 6, 2017

I asked a question on Stackoverflow with more details about Firefox specific errors, here.

@forberg

This comment has been minimized.

Show comment
Hide comment
@forberg

forberg Oct 6, 2017

@Maykonn Seems like navigator.doNotTrack is not null in Safari private mode on iOS

forberg commented Oct 6, 2017

@Maykonn Seems like navigator.doNotTrack is not null in Safari private mode on iOS

@jdc20181

This comment has been minimized.

Show comment
Hide comment
@jdc20181

jdc20181 Oct 15, 2017

Seems to be giving a false "Not private" - Meaning it should say private but it isn't - Using GeckFX (FireFox) .NET Control, I have the appropriate configurations for private mode, I just don't think its compatible with FF- Information on what I am using: https://bitbucket.org/geckofx/geckofx-45.0/issues/94/private-browsing

Not sure if its a JS issue or its something with the control/engine itself. My guess is it has something to do with the JS, with how it is detecting it via the code - or the version fo the engine itself although that should be ok.

Seems to be giving a false "Not private" - Meaning it should say private but it isn't - Using GeckFX (FireFox) .NET Control, I have the appropriate configurations for private mode, I just don't think its compatible with FF- Information on what I am using: https://bitbucket.org/geckofx/geckofx-45.0/issues/94/private-browsing

Not sure if its a JS issue or its something with the control/engine itself. My guess is it has something to do with the JS, with how it is detecting it via the code - or the version fo the engine itself although that should be ok.

@Maykonn

This comment has been minimized.

Show comment
Hide comment
@Maykonn

Maykonn Oct 24, 2017

I'm testing in various FF versions and from version 52 below don't working. Always returning as private browsing.
Safari latest version don't working too. I'm making tests with navigator.doNotTrack @forberg, thanks.

Maykonn commented Oct 24, 2017

I'm testing in various FF versions and from version 52 below don't working. Always returning as private browsing.
Safari latest version don't working too. I'm making tests with navigator.doNotTrack @forberg, thanks.

@mtage70

This comment has been minimized.

Show comment
Hide comment
@mtage70

mtage70 Oct 27, 2017

navigator.doNotTrack is just a Safari feature that asks sites not to track the user, it can be opted into by going to safari->preferences->privacy. navigator.doNotTrack is not null if the user has checked that option, it is not indicative of whether the user is in private mode or not.
I'm still investigating how to detect private browsing in Safari, if anyone else is interested in this feature we should join forces

mtage70 commented Oct 27, 2017

navigator.doNotTrack is just a Safari feature that asks sites not to track the user, it can be opted into by going to safari->preferences->privacy. navigator.doNotTrack is not null if the user has checked that option, it is not indicative of whether the user is in private mode or not.
I'm still investigating how to detect private browsing in Safari, if anyone else is interested in this feature we should join forces

@unlox775

This comment has been minimized.

Show comment
Hide comment
@unlox775

unlox775 Nov 29, 2017

Note: this does not seem to detect private browsing for iOS 11 Safari. It detects find in iOS 10 and 9 Safari.

unlox775 commented Nov 29, 2017

Note: this does not seem to detect private browsing for iOS 11 Safari. It detects find in iOS 10 and 9 Safari.

@ckimy

This comment has been minimized.

Show comment
Hide comment
@ckimy

ckimy Nov 30, 2017

Guys I found a solution.
You can detect private browsing in iOS 11 using below code. 😄
No matter user's DNT option is checked or not, you can detect private browsing mode properly.

var isPrivate = false;
try {
   window.openDatabase(null, null, null, null);
} catch (_) {
   isPrivate = true;
}
alert((isPrivate ? 'You\'re' : 'You aren\'t')  + ' in private browsing mode');

It worked for me perfectly. If it doesn't, please let me know.

ckimy commented Nov 30, 2017

Guys I found a solution.
You can detect private browsing in iOS 11 using below code. 😄
No matter user's DNT option is checked or not, you can detect private browsing mode properly.

var isPrivate = false;
try {
   window.openDatabase(null, null, null, null);
} catch (_) {
   isPrivate = true;
}
alert((isPrivate ? 'You\'re' : 'You aren\'t')  + ' in private browsing mode');

It worked for me perfectly. If it doesn't, please let me know.

@yimaneilicj

This comment has been minimized.

Show comment
Hide comment
@yimaneilicj

yimaneilicj Dec 15, 2017

@ckimy Think you very much! Let's dance!

@ckimy Think you very much! Let's dance!

@yimaneilicj

This comment has been minimized.

Show comment
Hide comment
@yimaneilicj

yimaneilicj Dec 15, 2017

@ckimy This code does not work in ios10 above

@ckimy This code does not work in ios10 above

@dkuryakin

This comment has been minimized.

Show comment
Hide comment
@dkuryakin

dkuryakin Dec 19, 2017

Hey, guys! There is my own version of snippet, based on original gist and some comments.
Check this out: https://jsfiddle.net/n0hnu5te/1/

It works for me in:

  • IE11
  • Chrome 63.0
  • Mobile Chrome 62.0 (Android)

Let's test it on different devices/oses/browsers!

dkuryakin commented Dec 19, 2017

Hey, guys! There is my own version of snippet, based on original gist and some comments.
Check this out: https://jsfiddle.net/n0hnu5te/1/

It works for me in:

  • IE11
  • Chrome 63.0
  • Mobile Chrome 62.0 (Android)

Let's test it on different devices/oses/browsers!

@ckimy

This comment has been minimized.

Show comment
Hide comment
@ckimy

ckimy Dec 21, 2017

@yimaneilicj Of course, this snippet is only for ios11. You can combine this snippet with another library/modules for other browsers. @dkuryakin had done this job for us! :)

ckimy commented Dec 21, 2017

@yimaneilicj Of course, this snippet is only for ios11. You can combine this snippet with another library/modules for other browsers. @dkuryakin had done this job for us! :)

@smarajitdasgupta

This comment has been minimized.

Show comment
Hide comment
@smarajitdasgupta

smarajitdasgupta Jan 11, 2018

Does anyone have a snippet that detects private mode on Safari on iOS11 as well as older Safari?

Update: Here is a pen that attempts to combine the storage and openDatabase try-catch blocks.
https://codepen.io/anon/pen/zpMZjp

The test window.openDatabase(null, null, null, null)
is not expected to throw an exception in Safari non-private browser mode, but it does (exception code 18).

In Safari new versions (11+ or iOS11+ Safari) normal browser mode, the second test (for openDatabase) enters the catch and isPrivate gets set to true. So, in Safari 11+ non-private mode is also detected as private mode. Anyone with a working solution on a server? It seems to work as expected in localhost, but window.openDatabase(null, null, null, null) throws exception both in normal and private mode when hosted in some server, as can be seen in the pen.

smarajitdasgupta commented Jan 11, 2018

Does anyone have a snippet that detects private mode on Safari on iOS11 as well as older Safari?

Update: Here is a pen that attempts to combine the storage and openDatabase try-catch blocks.
https://codepen.io/anon/pen/zpMZjp

The test window.openDatabase(null, null, null, null)
is not expected to throw an exception in Safari non-private browser mode, but it does (exception code 18).

In Safari new versions (11+ or iOS11+ Safari) normal browser mode, the second test (for openDatabase) enters the catch and isPrivate gets set to true. So, in Safari 11+ non-private mode is also detected as private mode. Anyone with a working solution on a server? It seems to work as expected in localhost, but window.openDatabase(null, null, null, null) throws exception both in normal and private mode when hosted in some server, as can be seen in the pen.

@gaplyk

This comment has been minimized.

Show comment
Hide comment
@gaplyk

gaplyk Mar 8, 2018

IE can return true when it's not when you have your restriction on your policy to disable indexedDB

gaplyk commented Mar 8, 2018

IE can return true when it's not when you have your restriction on your policy to disable indexedDB

@mtage70

This comment has been minimized.

Show comment
Hide comment
@mtage70

mtage70 Apr 17, 2018

I've noticed in Safari 11 normal browsing mode window.openDatabase(null, null, null, null) will throw an exception if a user has just cleared their history and refreshed.

mtage70 commented Apr 17, 2018

I've noticed in Safari 11 normal browsing mode window.openDatabase(null, null, null, null) will throw an exception if a user has just cleared their history and refreshed.

@bigmike7801

This comment has been minimized.

Show comment
Hide comment
@bigmike7801

bigmike7801 May 7, 2018

I'm using Safari 11.1 (11605.1.33.1.4) on OSX 10.11.6 and am getting "not private" when using a private window.

I'm using Safari 11.1 (11605.1.33.1.4) on OSX 10.11.6 and am getting "not private" when using a private window.

@bigmike7801

This comment has been minimized.

Show comment
Hide comment
@bigmike7801

bigmike7801 May 8, 2018

I switched line 82 to "true" and that seemed to fix it for me.

bigmike7801 commented May 8, 2018

I switched line 82 to "true" and that seemed to fix it for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment