A Wappalyzer-style Chrome extension that identifies the technologies behind any website: CMS, frameworks, libraries, analytics, CDN, hosting, servers and more, with confidence scores, versions and evidence.
- โจ Features
- ๐ธ Screenshots
- ๐ Quick start
- ๐งญ Using the extension
- ๐๏ธ Architecture
- ๐ง How detection works
- ๐ Confidence, versions & evidence
- ๐ Rules & categories
- ๐ฆ Optional GPL Wappalyzer pack
- ๐จ Icons & logos
- ๐ Permissions & privacy
- ๐งช Testing
- ๐ ๏ธ Scripts reference
- ๐ Project structure
- โ Adding a technology
- ๐ฉบ Troubleshooting
- ๐บ๏ธ Roadmap
- ๐ License
| Feature | Details | |
|---|---|---|
| ๐ | 7,500+ technologies | About 260 hand-written, tested rules, plus an optional pack of about 7,260 from the open Wappalyzer database |
| ๐ฏ | Confidence scoring | Every detection is Confirmed, Likely or Possible, based on how strong and how many its signals are |
| ๐ข | Version detection | Reported only when a reliable fingerprint gives one (e.g. WordPress 7.1.3, React 18.3.1, PHP 8.2.34) |
| ๐งพ | Evidence | Tap any technology to see exactly which signals matched and how much each contributed |
| ๐๏ธ | 33 categories | CMS, page builders, frameworks, libraries, analytics, CDN, hosting, security and more, shown in a two-column layout |
| ๐ | 11 signal sources | HTML, DOM, JS globals, script and link URLs, CSS text, HTTP headers, cookies (including HttpOnly), XHR/fetch calls, visible text and meta tags |
| โฑ๏ธ | Delayed-script aware | Finds scripts that LiteSpeed, WP Rocket and similar plugins hide until the user interacts |
| ๐ | Auto rescans | After the first interaction, about 4 s after load, and on single-page-app route changes |
| ๐ท๏ธ | Toolbar badge | Shows the number of technologies found on the current tab |
| ๐ผ๏ธ | Real logos | About 97% of technologies show a brand icon, bundled offline |
| ๐ | Light / dark / system themes | Remembered between sessions |
| โจ | Animated UI | Built with Motion; respects reduce motion |
| ๐ | Copy JSON | Export the full scan, including evidence |
| Light theme | Dark theme |
|---|---|
![]() |
![]() |
| Evidence for a detection | Resources tab |
|---|---|
![]() |
![]() |
- Node.js 22+ (developed on Node 24)
- Google Chrome 116+, or another Chromium browser such as Edge or Brave
- Microsoft Edge or Chromium: only needed for
npm run pack:fetch(to shrink oversized icons) and fornpm run test:e2e
cd extension
npm install
npm run pack:fetch # first time only: download Wappalyzer data and icons (~80 MB)
npm run build # โ dist/๐ก
pack:fetchis only needed once, and again whenever you want fresh Wappalyzer data. If you don't want the GPL pack at all, skip it and runnpm run build:coreinstead.
- Open
chrome://extensions. - Turn on Developer mode (top right).
- Click Load unpacked and select the
extension/distfolder. - Pin the extension (puzzle icon โ ๐).
- Click โป on the TechStack Detector card in
chrome://extensions. Chrome keeps running the old code until you do. - Reload the website tab. Header-based detections (servers, CDNs, security policies) only work for pages loaded after the extension.
- Check the popup header: it shows "Detector ยท v1.5.4".
| Area | What it shows |
|---|---|
| Header | Logo, version, theme toggle (โ๏ธ / ๐ / ๐ฅ๏ธ), rescan button |
| Site card | Favicon, hostname, URL, chips for Secure or Not secure, language and scan time |
| Overview tab | ๐งฉ Technologies, grouped by category in two columns, then ๐ Page details (title, description, generator, language, charset, URL); click a detail to copy it |
| Resources tab | Counts of scripts, external scripts, stylesheets, images, links and iFrames, plus an external-vs-inline script bar |
| JSON tab | The full scan result with syntax highlighting |
| Footer | ๐ Rescan and ๐ Copy JSON |
[logo] WordPress โญ 100
7.1.3 Confirmed
- Version chip: appears only when a fingerprint reliably exposed a version.
- Level: ๐ข Confirmed, ๐ต Likely, ๐ Possible, or via X when the technology was implied by another (e.g. PHP via WordPress).
- Ring: the confidence score (0โ100).
- Click the row to expand its evidence: each signal type (
META,HTML,SCRIPT,HEADER,JS,COOKIE,CSSโฆ), the matched value and its weight (+100).
This banner appears when the tab was loaded before the extension started or was reloaded. Headers are only recorded while a page loads, so reload the tab to detect web servers, CDNs and the services listed in the site's Content-Security-Policy.
โโโโโโโโโโโโโโโโโ Website tab โโโโโโโโโโโโโโโโโ
โ content.js (isolated world) โ
โ โข HTML, inline + decoded data: scripts โ
โ โข script/link URLs, meta, cookies, CSS โ
โ โข DOM probes, CSS variables, visible text โ
โ probeMainWorld() (page's MAIN world) โ
โ โข JS globals (window.React, Shopifyโฆ) โ
โ โข DOM expandos (__reactFiber$, __kโฆ) โ
โโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ COLLECT_FINGERPRINT
โโโโโโโโโโโโ SCAN_TAB โโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Popup โ โโโโโโโโโโโโถ โ Service worker (background.js) โ
โ (React) โ โโโโโโโโโโโโ โ โข webRequest: headers + requests โ
โโโโโโโโโโโโ result โ โข chrome.cookies: HttpOnly names โ
โ โข detector.detect(fingerprint) โ
โ โข tab cache + toolbar badge โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| Part | File | Role |
|---|---|---|
| โ๏ธ Service worker | src/background/serviceWorker.js |
The hub. Captures headers and network requests, runs scans, caches results per tab, updates the badge, routes messages. |
| ๐ Content script | src/content/content.js |
Runs in each page. Collects the fingerprint on request and triggers automatic rescans. |
| ๐ง Main-world probe | src/detector/scanners/jsScanner.js |
Injected into the page's own JavaScript context. Reads globals and DOM properties the content script can't see. |
| โ๏ธ Popup | src/popup/, src/components/ |
React 19 UI with Motion animations. |
| ๐ฌ Detector | src/detector/ |
Pure JavaScript (no Chrome APIs), so it's fully unit-testable. |
All messages are defined in src/shared/messages.js, and every response has the shape { ok: true, data } | { ok: false, error }.
| Message | From โ To | Purpose |
|---|---|---|
PAGE_READY |
content โ SW | Page loaded or interacted with, or the route changed. Run an auto-scan. |
COLLECT_FINGERPRINT |
SW โ content | Collect raw evidence for a scan |
SCAN_TAB |
popup โ SW | Full scan of the active tab |
GET_TAB_DATA |
popup โ SW | Cached result, for an instant render |
- Page load (
document_idle). - About 4 s later: apps often render late (loading screens, client-side redirects, chat widgets).
- 2.5 s after the first scroll, click or keypress, to catch scripts that optimisers delay until interaction.
- On single-page-app navigation (Navigation API, debounced).
- Whenever the popup opens, or you press Rescan.
Concurrent scans of the same tab are merged into one.
Detection works in four steps: collect evidence โ match rules โ score โ resolve relationships.
| Signal type | Source | Example |
|---|---|---|
html |
Page HTML, plus inline scripts that WP Rocket stores as base64 | /wp-content/plugins/ |
text |
Visible page text (innerText) |
Powered by โฆ |
scripts |
Contents of inline <script> tags |
_paq.push( |
scriptSrc |
<script src>, delayed data-src scripts and network requests |
cdn.shopify.com |
links |
<link href>, stylesheets and fonts |
fonts.googleapis.com |
css |
Text of same-origin stylesheets, including CSS merged by optimisers | font-family: "Font Awesome 5 Free" |
meta |
<meta name/property> |
generator: WordPress 7.1.3 |
headers |
HTTP response headers (webRequest) |
server: cloudflare, x-powered-by: PHP/8.2 |
cookies |
Cookie names only, including HttpOnly | laravel_session, _shopify_y |
xhr |
fetch/XHR/beacon request URLs | ip-api.com/json |
js |
Page globals (main world) | React.version, jQuery.fn.jquery |
jsPrefix |
Globals with generated names | webpackChunk_N_E, __sveltekit_x1y2 |
dom |
CSS selectors, attributes and element text | [ng-version], astro-island |
expando |
JS properties on DOM nodes | __reactFiber$, __vue_app__ |
cssVars |
Computed CSS custom properties | --tw-ring-offset-width (Tailwind) |
url |
Page URL | /wp-admin/ |
๐ก๏ธ Why three collection contexts? Content scripts run in an isolated world and can't see
window.Reactor React's DOM markers. Only the main-world probe can. HTTP headers aren't visible to page scripts at all, so the service worker reads them viawebRequest. HttpOnly cookies aren't indocument.cookie, so their names come fromchrome.cookies.
Each technology is a declarative rule, and the engine has no technology-specific code. A rule looks like this:
{
name: "Shopify",
category: "ecommerce",
website: "https://www.shopify.com",
color: "#95bf47",
headers: { "x-shopid": true }, // header exists
scriptSrc: [/cdn\.shopify\.com/i], // URL pattern
cookies: { _shopify_y: true }, // cookie name
js: { "Shopify.shop": { weight: 90 } }, // page global
meta: { generator: { pattern: /^Shopify/, version: "\\1" } }
}Each matching signal adds its weight, and the total is capped at 100. Strong, hard-to-fake signals weigh more than loose text matches:
| Signal | Default weight |
|---|---|
meta, headers |
100 |
expando |
90 |
js, xhr, cssVars, jsPrefix |
80 |
dom |
70 |
scriptSrc, links, cookies, css |
60 |
scripts |
50 |
html, text, url |
40 |
| Relationship | Example | Effect |
|---|---|---|
implies |
Next.js โ React; WordPress โ PHP, MySQL | Adds the implied technology, shown as via Next.js |
excludes |
Lodash excludes Underscore.js | Removes a look-alike |
requires |
a3 Lazy Load requires WordPress | Dropped unless the required technology is present |
requiresCategory |
Afterpay requires an ecommerce platform | Dropped unless something in that category is detected |
versionOnly |
_.VERSION for Lodash |
Supplies a version but can never detect on its own |
| Level | Score | Meaning |
|---|---|---|
| ๐ข Confirmed | โฅ 80 | One very strong signal, or several medium ones |
| ๐ต Likely | 50โ79 | Good evidence, not conclusive |
| ๐ Possible | < 50 | A single weak signal; treat as a hint |
Versions come from regex capture groups (version: "\\1"), including ternary templates like \1?GA4:UA. When several signals report a version, the most specific wins (18.3.1 beats 18). A version is never guessed: no reliable pattern means no version.
Evidence records the signal type, the key (e.g. the header name), the matched value (truncated) and its weight. Cookie values are never shown or stored; only the fact that a cookie exists.
There are 258 hand-written rules in 33 categories, one file per area in src/detector/rules/:
| File | Covers |
|---|---|
๐ cms.js |
WordPress, Drupal, Joomla, Ghost, Webflow, Wix, Squarespace, Framer, Duda, HubSpot CMS, TYPO3, Blogger, Contentful, Sanity, Strapi |
๐งฑ wordpress.js |
Page builders (Elementor, Divi, WPBakery, Bricksโฆ), themes (Astra, Kadence, Hello Elementorโฆ), plugins (Yoast, Rank Math, WP Rocket, LiteSpeed Cache, Contact Form 7โฆ) |
๐ ecommerce.js, ๐ณ payments.js |
Shopify, WooCommerce, Magento, BigCommerce, PrestaShop, Stripe, PayPal, Razorpay, Paddle, Klarnaโฆ |
โ๏ธ frameworks.js |
React, Next.js, Vue, Nuxt, Angular, Svelte, SvelteKit, Astro, Gatsby, Remix, Ember, Preact, SolidJS, Qwik, Lit, Meteorโฆ |
๐ฆ libraries.js |
jQuery (+ UI, Migrate), Lodash, Underscore, GSAP, Swiper, Slick, Lottie, Chart.js, Three.js, D3, PDF.js, htmxโฆ |
๐จ ui.js |
Tailwind, Bootstrap, Bulma, Foundation, MUI, Ant Design, Chakra, Mantine, Radix, DaisyUI, Lucide, Font Awesome, Google Fontsโฆ |
๐ analytics.js |
Google Analytics, GTM, Hotjar, Mixpanel, PostHog, Meta Pixel, Clarity, Amplitude, Matomo, Segment, Pendo, Vercel Analyticsโฆ |
๐ฌ media.js, ๐งฐ widgets.js |
YouTube, Vimeo; live chat (Intercom, Crisp, Zoho SalesIQโฆ), cookie banners, maps, ads, email marketing |
๐ก๏ธ security.js |
Cloudflare Turnstile, hCaptcha, reCAPTCHA, Sentry, Datadog |
๐ cdn.js, โ๏ธ hosting.js, ๐ฅ๏ธ servers.js |
Cloudflare, CloudFront, Fastly, Akamai; Vercel, Netlify, AWS, GCP, Azure, Hostinger, Kinsta; Nginx, Apache, LiteSpeedโฆ |
๐งโ๐ป languages.js |
Laravel, Django, Rails, Express, ASP.NET, PHP, Java, Node.jsโฆ |
โจ misc.js |
Open Graph, RSS, HTTP/3, HSTS, PWA, Priority Hints, webpack, Vite, Turbopack, MySQLโฆ |
Every one of these rules has a test fixture that proves it fires. The test suite fails if a rule is added without one.
For Wappalyzer-level coverage, the default build includes about 7,260 more technologies converted from the community-maintained Wappalyzer database, enthec/webappanalyzer.
โ๏ธ Licence: that data is GPL-3.0. A build that includes the pack must be distributed under GPL-3.0 terms, which means making the full source available. Use
npm run build:corefor a build with only our own rules and no GPL code: a 67 KB service worker instead of about 1.8 MB.
| Step | Command | What happens |
|---|---|---|
| 1 | npm run pack:fetch |
Downloads fingerprints, categories, icons and the LICENSE into vendor/webappanalyzer/. Oversized icons are rendered to 64 px PNGs. |
| 2 | npm run build |
Converts the data into src/detector/packs/wappalyzer.generated.js, then builds |
How the conversion works (scripts/lib/convertWappalyzer.mjs):
- ๐ Fields are mapped onto our signal types (
text,scripts,domattributes and text,propertiesโexpando, and so on). - ๐งฎ Modifiers are honoured:
\;version:\1and\;confidence:N;confidence:0becomes version-only. - ๐ค Our own rule wins where both define a technology. Upstream signals our rule lacks are merged in as a supplement, in pack builds only.
- ๐ท๏ธ Name aliases, e.g. Google Font API โ Google Fonts, so there are no duplicates.
- ๐ซ Server-side-only fingerprints are skipped (DNS, robots.txt, certificates).
- ๐ฉน
PACK_PATCHESfixes upstream fingerprints that misfired on real sites (e.g. Onsen UI matchingconsent.min.js, PyScript keyed on a Marketo cookie). - ๐พ The pack file is written atomically, so an interrupted import can't break the next build.
Icons are chosen in this order:
- ๐ท๏ธ Simple Icons brand logo (CC0). Only the icons actually used are bundled, in
src/components/techIcons.generated.js. - โก Symbols for web standards: HTTP/3 โก, Open Graph ๐, Priority Hints, HSTS ๐, PWA ๐ฑ.
- ๐ผ๏ธ The upstream icon from the GPL pack, copied into
dist/tech-icons/. Shown with<img>, so scripts inside an SVG can never run. - ๐ค A coloured letter tile as the last resort, with a colour derived from the name.
The result is a real icon for about 97% of technologies. Near-black logos (Next.js, Vercel, X) switch to the text colour in dark mode.
| Permission | Why it's needed |
|---|---|
host_permissions (http/https) |
Scan any site automatically and run the main-world probe |
scripting |
Inject the content script into tabs opened before install, and run the main-world probe |
webRequest |
Read HTTP response headers and the page's own network requests |
cookies |
Read the names of HttpOnly cookies (e.g. laravel_session) |
storage |
Per-tab scan cache (session) and the last scan |
activeTab |
Scan the current tab when you open the popup |
๐ Privacy:
- All detection runs locally. Nothing is sent to any server, and there's no analytics or tracking.
- Cookie values are never read into results, only their names.
- Results live in
chrome.storage.session: they're cleared when the browser closes, and per tab on navigation or close. - Requests made inside third-party iframes are ignored, so embedded widgets don't pollute a site's stack.
npm test # 351 unit, coverage, regression and pack tests (~3 s)
npm run test:e2e # real browser: 26 live websites in headless Edge (~5 min)| Suite | File | What it checks |
|---|---|---|
| ๐ง Engine | tests/detector.test.js |
Matching, scoring, implies, versions, false positives |
| ๐งฉ Coverage | tests/technologies.test.js |
Every rule fires on its fixture, extracts the right version, and never reports a version it can't support; plus false-positive look-alikes |
| ๐ Regression | tests/regression.test.js |
Restricted URLs, message contract, the probe surviving Chrome's serialisation, every category having an icon |
| ๐ฆ Pack | tests/pack.test.js |
Converter rules, supplements, CSP-based detection, no duplicates, nothing detected on a plain page |
| ๐ End-to-end | scripts/e2e.mjs |
Loads dist/ as a real extension, opens 26 live sites (Wappalyzer-compared), simulates a user interaction and checks the expected technologies |
npm run test:e2e -- calgameswanted # one site
E2E_DUMP=./dumps npm run test:e2e -- webvoltz # save full scan JSON for debuggingโน๏ธ Google Chrome 137+ ignores
--load-extension, so the e2e suite uses Edge or Chromium. SetBROWSER=<path>to choose one.
| Command | Description |
|---|---|
npm run build |
Import the pack (if fetched), generate icons, build into dist/ |
npm run build:core |
Build with our rules only (no GPL data or icons) |
npm run dev |
Rebuild on every save (then click โป in Chrome) |
npm test |
Unit, coverage and regression tests |
npm run test:e2e |
Real-browser test against live sites |
npm run pack:fetch |
Download or refresh the Wappalyzer data and icons |
npm run pack:import |
Re-convert the pack without a full build |
npm run icons:tech |
Regenerate Simple Icons logos (verbose report) |
npm run icons |
Regenerate the extension's own toolbar icons |
extension/
โโโ ๐ public/
โ โโโ manifest.json Manifest V3
โ โโโ icons/ toolbar icons (generated)
โโโ ๐ ๏ธ scripts/
โ โโโ build.mjs build pipeline (pages โ content IIFE)
โ โโโ e2e.mjs real-browser test
โ โโโ fetch-wappalyzer.mjs download pack data + icons
โ โโโ import-wappalyzer.mjs convert pack โ generated rules
โ โโโ lib/convertWappalyzer.mjs the converter (pure, tested)
โ โโโ optimize-icons.mjs render oversized icons to 64 px PNG
โ โโโ copy-tech-icons.mjs copy used icons into the build
โ โโโ generate-tech-icons.mjs Simple Icons โ techIcons.generated.js
โ โโโ generate-icons.mjs the extension's own icon
โโโ ๐งช tests/ unit, coverage, regression, pack (+ fixtures/)
โโโ ๐ฆ vendor/webappanalyzer/ upstream GPL data (after pack:fetch)
โโโ ๐ผ๏ธ docs/screenshots/
โโโ ๐ป src/
โโโ background/serviceWorker.js hub: scans, headers, requests, cookies, badge
โโโ content/ content script + page facts
โโโ detector/
โ โโโ engine/ detector, matcher, patterns, confidence, evidence, probe
โ โโโ scanners/ html, script, css, dom, cookie + main-world jsScanner
โ โโโ rules/ โ
one file per category โ all technologies live here
โ โโโ packs/ optional generated GPL pack (+ active.js switch)
โ โโโ categories.js 33 categories (label + order)
โ โโโ index.js rules + pack (with supplements) โ detector
โโโ popup/ App, hooks (scan, theme, clock), styles
โโโ components/ SiteHero, TechList, TechRow, tabs, ui/
โโโ services/ storage, tab cache, header & request stores, messaging
โโโ shared/messages.js message contract
โโโ utils/url.js
-
Add a rule to the matching file in
src/detector/rules/:{ name: "Acme Chat", category: "chat", website: "https://acme.chat", color: "#ff5a1f", scriptSrc: [{ pattern: /widget\.acme\.chat/i, weight: 100 }], js: { "AcmeChat.version": { pattern: /^([\d.]+)/, version: "\\1", weight: 90 } } }
-
Add a fixture to
tests/fixtures/technologies.js:"Acme Chat": { fp: { js: { "AcmeChat.version": "2.4.1" } }, version: "2.4.1" },
-
Add look-alikes to
NEGATIVEin the same file, if the technology has any. -
Check it: run
npm test, thennpm run build, then reload the extension.
Rule reference:
| Key | Type | Notes |
|---|---|---|
html text scripts css scriptSrc links xhr url |
PatternSpec[] |
Matched against that source |
meta headers |
{ name: PatternSpec } |
Names are case-insensitive |
cookies |
{ name: PatternSpec } |
Exact, glob_* or /regex/; HttpOnly included |
js |
{ "dot.path": PatternSpec } |
Read in the page's main world |
dom |
{ selector: { attribute?, pattern?, version? } } |
attribute: "#text" = element text |
expando jsPrefix cssVars |
string[] |
DOM properties, global-name prefixes, CSS variables |
implies excludes requires |
string[] |
Technology names |
requiresCategory |
string[] |
Category ids |
A PatternSpec is a RegExp, a literal string, true (the key only has to exist), or { pattern, weight, version, versionOnly }.
| Problem | Fix |
|---|---|
| ๐ข Header shows an old version | Click โป in chrome://extensions. If needed, remove the extension and Load unpacked dist/ again. |
| ๐ Fewer technologies than expected | Reload the tab, then scroll or click once and reopen the popup. Many sites load scripts only after interaction. |
| Reload the tab. Headers are only recorded during page load. | |
| ๐งฑ Build says "GPL pack data missing" | Run npm run pack:fetch, or use npm run build:core. |
๐ pack:fetch says "could not reach GitHub" |
Check your connection, VPN or proxy. Your existing vendor/ data is untouched, and npm run build still works. |
| ๐ซ Popup says "This page can't be scanned" | Chrome blocks extensions on chrome:// pages, the Web Store and file:// URLs. |
| ๐งช e2e can't find a browser | Install Edge or Chromium, or set BROWSER=<path>. |
| Phase | Scope | Status |
|---|---|---|
| 1๏ธโฃ Foundation | MV3, React popup, content script, service worker, messaging, storage | โ |
| 2๏ธโฃ Detection engine | Fingerprints, rules, confidence, evidence | โ |
| 3๏ธโฃ 100+ technologies | 258 own rules + optional ~7,260-technology pack, logos, 26-site e2e | โ |
| 4๏ธโฃ MERN SaaS | Express + MongoDB, JWT, dashboard, scan history | โณ Next |
| 5๏ธโฃ Advanced | Bulk scanning, export, technology-change monitoring | ๐ |
| 6๏ธโฃ Production | Queues, rate limits, Chrome Web Store, subscriptions | ๐ |
- Our own code and rules: choose and add a licence (e.g. MIT) before publishing.
- The optional Wappalyzer pack (
vendor/webappanalyzer,src/detector/packs/wappalyzer.generated.js, upstream icons): GPL-3.0. Seevendor/webappanalyzer/LICENSE. Builds made withnpm run build:corecontain none of it. - Simple Icons logos: CC0 1.0. Brand logos remain trademarks of their owners.
Made with โค๏ธ for learning Chrome extensions ยท ๐ TechStack Detector



