A website usually cannot ask Chrome for your full extension list. But it may be able to test whether specific extension resources are reachable.
One signal means little. Many signals together can reveal patterns: work tools, wallets, accessibility tools, job search tools, or sensitive proxy interests.
Demo result
All checks run locally in this browser. Nothing is sent anywhere.
Not run yet
Open the menu or press Run demo to simulate extension probing with fake extension IDs.
Check your own browser
Paste extension IDs from chrome://extensions. The page tests common exposed resource paths locally.
Needs IDs
Paste extension IDs or chrome-extension:// URLs from your own browser.
A clean result is not a guarantee. It only means the IDs you pasted did not expose the common resources tested here.
Copyable console snippet
Paste it into DevTools Console, enter your own extension IDs, and it renders a local result panel.
(async () => {
console.clear();
const pasted = prompt(
"Paste extension IDs or chrome-extension:// URLs from your own browser. The check stays local.",
""
);
if (!pasted) {
console.log("No extension IDs entered. Nothing was tested.");
return;
}
const candidateResources = [
"icon.png",
"icons/icon.png",
"icons/icon-128.png",
"icons/128.png",
"assets/icon.png",
"images/icon.png",
"logo.png",
"assets/logo.png"
];
function extractExtensionIds(text) {
return Array.from(new Set((text.match(/\b[a-p]{32}\b/g) || []))).slice(0, 12);
}
function testResource(url, timeout = 800) {
return new Promise((resolve) => {
const img = new Image();
let settled = false;
const done = (result) => {
if (settled) return;
settled = true;
img.onload = null;
img.onerror = null;
resolve(result);
};
const timer = setTimeout(() => done(false), timeout);
img.onload = () => {
clearTimeout(timer);
done(true);
};
img.onerror = () => {
clearTimeout(timer);
done(false);
};
img.src = url;
});
}
function renderPanel(results) {
document.getElementById("extension-fingerprint-snippet-panel")?.remove();
const foundCount = results.filter((item) => item.detected).length;
const panel = document.createElement("section");
panel.id = "extension-fingerprint-snippet-panel";
panel.style.cssText = [
"position:fixed",
"right:16px",
"bottom:16px",
"z-index:2147483647",
"width:min(420px,calc(100vw - 32px))",
"max-height:70vh",
"overflow:auto",
"border:1px solid #cfd6df",
"border-radius:10px",
"padding:14px",
"background:#fbfaf7",
"color:#182033",
"box-shadow:0 18px 48px rgba(20,26,40,.18)",
"font:14px/1.45 system-ui,-apple-system,Segoe UI,sans-serif"
].join(";");
panel.innerHTML =
'<div style="display:flex;align-items:start;justify-content:space-between;gap:12px">' +
'<div><strong style="font-size:16px">Extension probe result</strong>' +
'<div style="color:#596275;margin-top:2px">Local check. No network upload.</div></div>' +
'<button type="button" aria-label="Close" style="border:0;background:#e8edf4;border-radius:7px;padding:4px 8px;cursor:pointer">x</button>' +
'</div>' +
'<p style="margin:12px 0;color:#30384c">Possible reachable resources: <strong>' + foundCount + '</strong> of <strong>' + results.length + '</strong> IDs.</p>' +
'<div style="display:grid;gap:8px">' +
results.map((item) => {
const status = item.detected ? "Reachable resource found" : "No exposed resource found";
const color = item.detected ? "#8a4b00" : "#475569";
const resource = item.reachableUrls[0] || "Tested common icon/resource paths";
return (
'<div style="border:1px solid #dfe4ea;border-radius:8px;padding:10px;background:#fffdf8">' +
'<div style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow-wrap:anywhere">' + item.id + '</div>' +
'<div style="margin-top:5px;color:' + color + ';font-weight:700">' + status + '</div>' +
'<div style="margin-top:4px;color:#647084;font-size:12px;overflow-wrap:anywhere">' + resource + '</div>' +
'</div>'
);
}).join("") +
'</div>' +
'<p style="margin:12px 0 0;color:#596275;font-size:12px">A normal site cannot list all extensions, but it can test guessed extension IDs and exposed resources. Enough small signals can become a privacy fingerprint.</p>';
panel.querySelector("button")?.addEventListener("click", () => panel.remove());
document.body.appendChild(panel);
}
const ids = extractExtensionIds(pasted);
if (ids.length === 0) {
console.warn("No Chrome extension IDs found. Use chrome://extensions, enable Developer mode, then copy one or more IDs.");
return;
}
const results = [];
for (const id of ids) {
const testedUrls = candidateResources.map((resource) => "chrome-extension://" + id + "/" + resource);
const checks = await Promise.all(testedUrls.map(async (url) => [url, await testResource(url)]));
const reachableUrls = checks.filter(([, detected]) => detected).map(([url]) => url);
results.push({
id,
testedUrls,
reachableUrls,
detected: reachableUrls.length > 0
});
}
renderPanel(results);
const found = results.filter(r => r.detected);
console.log("%cBrowser extension fingerprinting demo", "font-size: 18px; font-weight: bold;");
console.log("This demo does not send data anywhere.");
console.table(results.map((item) => ({
id: item.id,
testedResources: item.testedUrls.length,
reachableResources: item.reachableUrls.length,
firstReachableUrl: item.reachableUrls[0] || ""
})));
console.log("\\nSummary:");
console.log("Tested " + results.length + " user-provided extension IDs.");
console.log("Possible reachable resources: " + found.length);
if (found.length > 0) {
console.warn("Some extension resources appeared reachable. A tracker could combine this with other signals.");
} else {
console.log("No exposed resources were detected in the common paths tested. That does not prove the extensions are absent.");
}
console.log("\\nWhy this matters:");
console.log("- A website usually cannot ask Chrome for your full extension list.");
console.log("- But it can test guessed extension IDs and exposed resources.");
console.log("- Some extension categories can become sensitive proxy signals: work, health, accessibility, finance, religion, politics, or job searching.");
console.log("- This does not prove a website knows those traits. It shows how extension presence can become a proxy signal.");
})();
01
How to use Browser Extension Fingerprinting Demo
01
Run the safe built-in demo
Use Run demo to see six fake categories tested. The fake IDs illustrate the mechanism without acting as a real extension fingerprint list.
02
Read the result boundary
A reachable result shows that the tested resource was exposed. A not-found result is less conclusive because the path may differ or browser policy may have blocked it.
03
Optionally supply your own IDs
Copy IDs from your own Chromium extension-management page and paste up to 12 into the self-check. The site does not provide IDs for real extensions.
04
Run the local self-check
The page tests a small predefined set of generic resource locations for only the IDs you supplied and lists any reachable result.
05
Use the snippet only on your own browser
If you copy the console version, inspect it and run it only with your own IDs. It does not create a general extension inventory.
02
Formats, compatibility, and limits
Inputs
No input for the built-in mode, which uses deliberately fake extension IDs.
Optional Chrome-style extension IDs or `chrome-extension://` URLs copied from the user’s own browser, capped at 12 unique IDs.
A manual Run action; the tool does not probe user-supplied IDs automatically on page load.
Outputs
Per-target reachability results for fake demo targets or user-supplied IDs.
A count of IDs for which at least one tested resource appeared reachable.
An optional copyable local console snippet; no extension database, inferred profile, or downloadable dataset.
Compatibility
Meaningful live checks depend on a Chromium-based browser and its extension resource and security rules.
Other browsers may reject Chrome-style extension URLs or return only negative results.
The copied snippet is intended for a desktop browser where the user can inspect and open developer tools.
Known limits
The tool cannot request or enumerate the browser’s complete installed-extension list.
A failed request does not prove an extension is absent; the extension may expose no resource at the generic locations tested, or the browser may block the check.
A reachable resource is only one browser signal and does not prove a person’s identity, beliefs, health, finances, employment status, or other sensitive trait.
The built-in target IDs are fake by design, and the tool contains no real-extension names, IDs, package data, or resource-path inventory.
The optional self-check is limited to user-supplied Chrome-style IDs and a small generic resource set.
03
Privacy and processing
The built-in demonstration uses fake IDs and therefore does not inspect the user’s real extension set.
User-pasted IDs remain in page memory, are checked through local Chrome-style resource URLs, and are not uploaded or persisted by the tool.
The page does not save results in cookies, local storage, or IndexedDB.
The copyable snippet is visible for inspection and states that it does not send its result anywhere.
04
What a local extension-resource check can and cannot show
For each fake or user-supplied Chrome-style ID, the demo makes local image requests to a small, fixed set of generic extension resource locations. It watches for load and error events and stops each check after a short timeout. A successful load suggests that the page can reach one resource. Failure has several possible meanings: the ID may be absent, the resource may not be web-accessible, the path may differ, or browser policy may have blocked the request. The implementation intentionally avoids research into real extension packages and contains no deployable target inventory.
05
Questions about Browser Extension Fingerprinting Demo
Can a website list every extension I have installed?
Normally, no. This demonstration covers a narrower risk: a site may guess a specific Chrome-style ID and attempt to load a resource that the extension has made accessible.
Does the built-in demo test real browser extensions?
No. Its six targets use fabricated IDs and fictional names. The page does not contain a database of real extension IDs or resources.
What does a negative result mean?
It means that none of the small set of generic resource locations loaded for that ID. It does not prove the extension is absent, and it says nothing about resources at other paths.
Are IDs from my self-check uploaded or stored?
The component keeps pasted IDs and results in memory for the current session and issues Chrome-style local resource checks. It does not submit or persist them.