Headless Browser Detection (Headless Chrome)

Google changed his Headless Chrome Browser (a derivate of the Chromium browser) and removed the information about “Headless” (“HeadlessChrome”) from the User Agent. Because of that, it is no possible to detect this browser as an headless browser.

This maybe matched also the Microsoft Edge Headless Browser, because this is also a derivate of the Chromium browser.

The information about “Headless” can maybe extract from the JavaScript navigator.userAgentData, but this is an experimental technology.

Here a solution to detect hidden headless browser.

Basic infos:
: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/getHighEntropyValues
: https://learn.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-guidance
: https://developer.chrome.com/docs/privacy-security/user-agent-client-hints

In the browser standards, the User Agent is saved in navigator.userAgent as an string.
Example (headless, but out of date):

Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/100.0.4889.0 Safari/537.36

The new one is navigator.userAgentData.

Example of navigator.userAgentData (non headless):

// navigator.userAgentData

{
  "brands": [
    {
      "brand": "Not_A Brand",
      "version":"8"
    },
    {
      "brand": "Chromium",
      "version":"120"
    },
    {
      "brand": "Microsoft Edge",
      "version":"120"
    }
  ],
  "mobile": false,
  "platform": "Windows"
}

Example of navigator.userAgentData.getHighEntropyValues() (non headless):

// navigator.userAgentData.getHighEntropyValues(["fullVersionList"])

{
   "brands":[
      {
         "brand":" Not A;Brand",
         "version":"99"
      },
      {
         "brand":"Chromium",
         "version":"98"
      },
      {
         "brand":"Google Chrome",
         "version":"98"
      }
   ],
   "fullVersionList":[
      {
         "brand":" Not A;Brand",
         "version":"99.0.0.0"
      },
      {
         "brand":"Chromium",
         "version":"98.0.4738.0"
      },
      {
         "brand":"Google Chrome",
         "version":"98.0.4738.0"
      }
   ],
   "mobile":false
}

A Solution for Matomo: A function to detect “Headless” in this values:

function browserheadless() {
	var result = false;
	if (typeof navigator !== "undefined") {
		if (typeof navigator.userAgent !== "undefined") {
			if (navigator.userAgent.toLowerCase().includes("headless") === true) {
				result = true;
			}
		}
		if (typeof navigator.userAgentData !== "undefined") {
			if (typeof navigator.userAgentData.brands !== "undefined") {
				for (var i = 0; i < navigator.userAgentData.brands.length; i++) {
					if (navigator.userAgentData.brands[i].brand.toLowerCase().includes("headless") === true) {
						result = true;
					}
				}
			}
			if (typeof navigator.userAgentData.getHighEntropyValues() !== "undefined") {
				if (typeof navigator.userAgentData.getHighEntropyValues().brands !== "undefined") {
					for (var i = 0; i < navigator.userAgentData.getHighEntropyValues().brands.length; i++) {
						if (navigator.userAgentData.getHighEntropyValues().brands[i].brand.toLowerCase().includes("headless") === true) {
							result = true;
						}
					}
				}
				if (typeof navigator.userAgentData.getHighEntropyValues(["fullVersionList"]).fullVersionList !== "undefined") {
					for (var i = 0; i < navigator.userAgentData.getHighEntropyValues(["fullVersionList"]).fullVersionList.length; i++) {
						if (navigator.userAgentData.getHighEntropyValues(["fullVersionList"]).fullVersionList[i].brand.toLowerCase().includes("headless") === true) {
							result = true;
						}
					}
				}
			}
		}
	}
	return result;
}

if (browserheadless() === false) {
	// Here the Matomo Tracking Script Snippet
}

untested. use at you own risk.
consider: the used navigator.userAgentData is an experimental technology.

1 Like

Hi @melbao
Do you know if this has been taken into account bu Matomo browser detection? If not it would be great to create a ticket in GitHub for that…

@heurteph-ei , the Matomo Device Detector has it included and put it in the URL Querystring as uadata, but the Device Detector don’t exclude Headless Browser from tracking. For this it exist the Plugin Tracking Spam Prevention, and this hasn’t included it:

So, the question is, how include it?

1 Like

TrackingSPAMPrevention will, after this ticket is fixed, use the client hints (parameter uadata) to assess, if this is a headless browser.

1 Like