Can you determine if Chrome is in incognito mode via a script?
Asked Answered
C

11

139

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.

Coelostat answered 26/5, 2010 at 0:4 Comment(11)
And you have to remember that the user must allow incognito mode for the extension manual. By default, everything is true.Taxiplane
@Mohamed: The user having to allow it would be me, so that won't be a problem :)Coelostat
Wait, I just realised I didn't make myself clear - I meant with a user script. I'll keep this question alive, as it has some useful answers (thanks), but I'll ask another question with extra clarification.Coelostat
#2861379Centre
@aren Why not make it easy for script to determine if being viewed in incognito/private mode? The page is still executed in the browser sandbox and (theoretically) can't do anything to circumvent the privateness of the page visit, yet the programmer could add value by knowing it's incognito. Am I missing something?Echikson
@PeteAlvin Or the site could detract value by detecting incognito. Boston Globe's site won't display its articles in incognito, preventing the user from circumventing their free articles quota. In general, I prefer a site know less about me.Worrisome
Exists a lib to detect browsing mode: github.com/Maykonn/js-detect-incognito-private-browsing-paywallNichani
Yes, but should you? (Hint: no you shouldn't)Bathesda
Unless you want to, in which case it's fine.Coelostat
"Chrome will likewise work to remedy any other current or future means of Incognito Mode detection." quote from Google blog - so any solution is likely to stop working, at some point in the future.Springlet
@TiagoRangeldeSousa I own the repo detectIncognito mentioned below. Have you tested that, and if so did it not work? Presently I am able to detect incognito on all modern browsers.Aurist
M
252

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"));
    }
Molloy answered 6/1, 2015 at 19:9 Comment(29)
This is brilliant! Thank you soo much! The only problem is that it's asynchronous and since I have a situation where I have to wait for that check it slows down the page load by a few miliseconds.Fancied
@Fancied do the check once and then set a cookie. A few ms on the first page load should not be noticeable in the larger scheme of things.Molloy
@Molloy Thanks for that information. But I think in my case it wouldn't help because the user can't use all functionalities on the page if incognito mode is enabled. After the check a notification about that is displayed that the user if he wants to use all functionalities he should leave incognito mode and refresh the page. So the check every time he enters the page makes sense.Fancied
@Fancied Again, if a few ms on a single page is a concern, you are doing something wrong. The cost of establishing a connection to a website (DNS + TCP + TLS) should dwarf your ms hit on the filesystem API call.Molloy
@Alok: Well, in the end the detection didn't work in all browsers anyway. Please check out: #2861379 What I did was to check if local storage and cookies are available and made the site work with some more if and else conditions. That worked best for me and the users.Fancied
@Fancied jsfiddle.net/w49x9f1a still works fine for me in latest Chrome on mac osx. weird...Molloy
@Alok: I think it fails for Safari's Incognito Mode on Iphone 7. Safari - the new Internet Explorer ;)Fancied
this is not working anymore (maybe it worked in the past)Bumble
@AlexandruRada still works for me. Again, this is for Chrome on the desktop. It's not going to work with other browsers/envs.Molloy
I tried at Chrome. It works, but the logic is backwards.Furtherance
I had the same experience as Lucas, it seems to work if you reverse the console.log.bind statements (I used an anonymous function there to set a variable to true or false)Wetmore
Thinking this is out of date. When I log out the fs instance, I get an instance of webkitRequestFileSystem in both contexts.Diphenyl
Exists a lib to detect browsing mode: github.com/Maykonn/js-detect-incognito-private-browsing-paywallNichani
Does this fail any errors with older browsers or other browsers other than Chrome/Firefox? I want to be sure it doesnt cause errors in like IE etcFastening
This is for Chrome ONLY!! so.. do not use it for OTHER browsersOutdistance
This is going to go away soon thanks to these commitsMiriam
Semi-related to the question, navigator.serviceWorker is undefined in Firefox' incognito mode. Might be worth poking at Chrome's serviceWorker API to see if any differences pop up.Molloy
Funny enough @Molloy - last month Chrome tried to beat your script, but failed!Odontograph
Try running Chrome's incognito mode with disable-web-security flag and the FileSystem API will still work on itDamara
Meanwhile, Chrome v75 came out and beat the script (if you run JSFiddle it'll look not private when it is). But it only beats the script if the user enables the aforementioned secret opt-it setting. I assume by v76 this setting will be enabled by default and render this script obsolete.Odontograph
Chrome v76 came up and just beats the script by default, which means this answer is no longer valid. But I read somewhere there's a limit on how much file size Chrome gives access to in InCogntio mode. So you may try to adjust the script to use this in order to beat Chrome again.Odontograph
Correct, see mishravikas.com/articles/2019-07/….Molloy
fail in chrome v 77.0.3865.90Immeasurable
const {usage, quota} = await navigator.storage.estimate(); if(quota < 120000000){//Incognito} else {// not Incognito} This thing is still working in latest chrome browser.Fumikofumitory
This solution is no longer valid in newer chrome versions. I verified in Version 84.0.4147.135 (Official Build) (64-bit).And this is no longer the case.Overblouse
try https://mcmap.net/q/165923/-can-you-determine-if-chrome-is-in-incognito-mode-via-a-script for more recent Chrome versions.Molloy
It seems to be ok for my machine which uses Chrome Version 87.0.4280.67 (Official Build) (x86_64), it evens work when I changed the size to 0. fs(window.TEMPORARY, 0, ...Eleen
This fails on Chrome 106 (Ubuntu 20.04)Credo
This answer is no longer correct. The ability to detect Incognito was removed in Chrome 80 (File System API disabled) and 81 (File System quota limit). See blog.google/outreach-initiatives/google-news-initiative/…Mound
B
25

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')
}
Bromine answered 10/8, 2019 at 2:48 Comment(1)
Unfortunately, this doesn't work now: for example, in newest MS's EDGE I have 224053418 bytes in so called Private mode. In Chrome I see 234549212 bytes in Incognito mode.Widener
R
20

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.

Recrystallize answered 26/5, 2010 at 0:15 Comment(4)
That's pretty cool, i didn't realise that incognito mode doesn't highlight visited links. This requires user to click a link though.Magneto
No it doesn't, the iframe "clicks" the link.Anergy
This doesn't actually work since most browsers don't expose :visited style information through javascript. The CSS interface will say as if the link has the non-visited color. This is a security measure that has been in WebKit- and Gecko browsers at least since 2010. This was to protect the user's history (e.g. one could try all possible URLs, and send the visited ones to a third party. This way one could get access to tokens in urls and what not).Pisano
Official Mozilla article explaining the privacy changes regarding :visited: hacks.mozilla.org/2010/03/…Comines
M
7

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.

Magneto answered 26/5, 2010 at 0:16 Comment(1)
Recently, it disables all extensions except those extensions which were specifically marked by the user as incognito safe.Ringtailed
A
6

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.

Aurist answered 23/10, 2021 at 14:9 Comment(0)
J
5

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.

Juno answered 26/5, 2010 at 7:24 Comment(0)
M
2

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

Malachy answered 30/3, 2018 at 14:28 Comment(6)
This looks very interesting and is a form of Javascript that I'm unfamiliar with and seems unsupported by my Netbeans 8.2 IDE in Windows 10. But if I can get it to work, I'm also curious: could I replace reject('Check incognito failed') with resolve(false) if I want isIncognito to be a boolean that is only ever true or false? Thanks.Siegel
I see Syntax Error: await is a reserved word, and I think await always needs to be within an async function. So I think this code doesn't work.Siegel
reject would raise an exception if it is called, and a value would not be returned, so isIncognito would always be true or false the way the code is written. However, you would also be able to use a 'resolve' there if you want a value returned. And it might depend on how the environment handles await in the global scope? It works on the chrome console in the global scope, but you could try wrapping everything in (async function() { 'everything here' })(); so it is run inside an async functionMalachy
Alternatively, you could just save the promise to isIncognito instead of the result, and run it afterwards in an async function: let isIncognito = new Promise( ...etc then somewhere else you'd have a function like (async function() { if( !(await isIncognito) ) {alert('not incognito') } })();Malachy
This answer is invalidated in chrome 76+Anabel
Do not work for me.Pean
D
1

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/

Denis answered 30/9, 2022 at 9:56 Comment(1)
Not sure how this is working for you in v105. In Version 106.0.5249.103 (Official Build) (x86_64), an incognito tab is showing "this is a normal tab". Detecting incognito mode via quota was fixed in 2020: bugs.chromium.org/p/chromium/issues/detail?id=1017120Mound
M
0

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') });
Malachy answered 30/4, 2017 at 11:17 Comment(0)
E
0

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)
Engraft answered 20/10, 2018 at 11:44 Comment(1)
this returns false in chrome incognitoRalph
S
-4

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.");  }
Sodality answered 2/5, 2021 at 8:19 Comment(2)
Your fiddle works for me but testing on localhost in incognito tells me that I'm not in incognitoBadgett
@S.Ramjit This script has to be executed in a webpage, which is in an iframeSodality

© 2022 - 2024 — McMap. All rights reserved.