Skip to content

Instantly share code, notes, and snippets.

@cou929
Last active May 1, 2024 21:07
Show Gist options
  • Select an option

  • Save cou929/7973956 to your computer and use it in GitHub Desktop.

Select an option

Save cou929/7973956 to your computer and use it in GitHub Desktop.
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>
@ckimy

ckimy commented Nov 30, 2017

Copy link
Copy Markdown

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

Copy link
Copy Markdown

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

@yimaneilicj

Copy link
Copy Markdown

@ckimy This code does not work in ios10 above

@dkuryakin

dkuryakin commented Dec 19, 2017 •

Copy link
Copy Markdown

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

ckimy commented Dec 21, 2017 •

Copy link
Copy Markdown

@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

smarajitdasgupta commented Jan 11, 2018 •

Copy link
Copy Markdown

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

gaplyk commented Mar 8, 2018

Copy link
Copy Markdown

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

@mtage70

mtage70 commented Apr 17, 2018

Copy link
Copy Markdown

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

Copy link
Copy Markdown

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

bigmike7801 commented May 8, 2018 •

Copy link
Copy Markdown

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

@Maykonn

Maykonn commented Jul 20, 2018 •

Copy link
Copy Markdown

@tormod17

tormod17 commented Nov 6, 2018

Copy link
Copy Markdown

Has anyone found a solution for chrome IOS 12 + for detecting private mode?

@gremz

gremz commented Nov 16, 2018

Copy link
Copy Markdown

@tormod17 where/how are you trying to attempting this check? Any chance you're doing this in an iframe?

@gu10214

gu10214 commented Feb 8, 2019

Copy link
Copy Markdown

False positive on iOS 12+ as tormod17 mentioned, are we playing whack a mole here with each iOS release/patch?

@narcisso

narcisso commented Apr 2, 2019

Copy link
Copy Markdown
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);
});

@0c0c0f

0c0c0f commented May 13, 2019

Copy link
Copy Markdown
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);
});

window chrome the result is error!

@aneudy1702

aneudy1702 commented Mar 26, 2020 •

Copy link
Copy Markdown

This does not seem to work anymore

https://twitter.com/paul_irish/status/1138471166115368960

for desktop you can use

https://mishravikas.com/articles/2019-07/bypassing-anti-incognito-detection-google-chrome.html

(async () => {
  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");
  }
})();

@esemeniuc

Copy link
Copy Markdown

Incorrect reading for Chrome 81 when not in incognito

@mritgupta

mritgupta commented Sep 14, 2020 •

Copy link
Copy Markdown

It's able to detect private mode in chrome 85

@kuldeepchopradotnet

Copy link
Copy Markdown

not working with latest version

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