Every site leaks its stack. Response headers, global variables, HTML attributes, file paths, cookie names, the exact shape of a bundle URL. Reading those signals is a five-minute job once you know where to look, and it's genuinely useful: competitive research, deciding whether an integration will be painful, working out why a site you've inherited behaves strangely, or checking which third parties a page is loading.
Start with the response headers
Open DevTools, Network tab, reload, click the document request. The infrastructure usually announces itself:
| Header | Tells you |
|---|---|
Server | nginx, Apache, cloudflare, AmazonS3, Vercel |
X-Powered-By | PHP, Express, ASP.NET, Next.js — often left on by accident |
X-Vercel-Id, X-Nf-Request-Id, X-Amz-Cf-Id | Vercel, Netlify, CloudFront |
CF-Ray, CF-Cache-Status | Cloudflare, plus whether the request was a cache hit |
X-Drupal-Cache, X-Generator | CMS, sometimes with a version number |
Set-Cookie names | wordpress_*, laravel_session, _shopify_*, ASP.NET_SessionId, JSESSIONID |
X-Shopify-Stage, X-Sorting-Hat-* | Shopify |
Cookie names are one of the most reliable fingerprints, because they're set by the framework rather than chosen by the developer. The HTTP headers guide covers how to read all of these properly.
Then the HTML
Ctrl+U for the raw source, and look for:
<meta name="generator">— WordPress, Drupal, Hugo, Ghost and many others announce themselves outright<div id="__next">(Next.js),<div id="root">(Create React App),data-reactroot,ng-version(Angular),data-v-attributes (Vue scoped styles),data-svelte-h(Svelte)/wp-content/or/wp-json/in asset paths — WordPress, unambiguously/_next/static/,/_nuxt/,/_astro/,/build/_shared/— the framework's build output directorycdn.shopify.com,cdn11.bigcommerce.com,static.wixstatic.com— hosted platforms- Class name patterns — long strings of utility classes suggest Tailwind;
col-md-6andbtn-primarysuggest Bootstrap
Then the JavaScript globals
Open the Console and check what's hanging off window:
// Framework markers
window.React && React.version;
window.__NEXT_DATA__; // Next.js, including the build id and page props
window.__NUXT__; // Nuxt
window.ng; // Angular
window.__VUE__ || window.Vue;
window.Shopify; // Shopify storefront
window.wp; // WordPress admin/block editor assets
// Analytics and tag managers
window.dataLayer; // GTM
window.gtag || window.ga; // Google Analytics
window.fbq; // Meta pixel
window.Intercom, window.zE; // Support widgets
// Everything unusual on window, in one line
Object.keys(window).filter(k => !(k in globalThis.constructor.prototype));__NEXT_DATA__ is particularly rich: it contains the page props, the build ID and the route, which tells you not just that it's Next.js but roughly how the site is structured.
Production builds strip a lot of this. If globals come back empty, check for a __REACT_DEVTOOLS_GLOBAL_HOOK__ and look at the bundle filenames instead — chunk naming conventions differ enough between bundlers to be a decent tell.
Then the network requests
The Network panel's domain list is a map of every vendor the site pays for. Sort by domain and read down:
googletagmanager.com,google-analytics.com— the usual analyticssegment.com,mixpanel.com,amplitude.com— product analyticshotjar.com,clarity.ms,fullstory.com— session recordingintercom.io,crisp.chat,zendesk.com— supportjs.stripe.com,paypal.com,checkout.paddle.com— paymentsfonts.googleapis.com,use.typekit.net— font providerscdn.jsdelivr.net,unpkg.com,cdnjs.cloudflare.com— library CDNs, often with the version in the URL
That last category is a gift, because https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js tells you the exact dependency and version. It's also how you find out a site is loading a library with a known vulnerability.
Doing it in one click
All of the above is manual reconnaissance. It's worth knowing because it works on anything and doesn't depend on someone else's fingerprint database, but for a first pass it's slow.
The Technology Detector category in Web Developer Tools automates exactly these checks and reports what it finds: Frontend Frameworks (React, Vue, Angular and friends), CSS Frameworks (Tailwind, Bootstrap), CMS Platforms (WordPress, Shopify), Analytics Providers, Payment Providers, CDN Providers, Hosting Platforms and Web Servers. Each one reads the signals described above rather than sending the URL anywhere, so it works on internal and staging sites too — which is the main limitation of the online lookup services.
Its JavaScript category adds Detect Framework, View Loaded JavaScript Files, View ES Modules and a Module Dependency Viewer that extracts import specifiers, which is a good way to see the module graph of a modern site without reading a bundle.

Reading the results critically
Detection is inference, and inference goes wrong in specific ways:
- 1Leftovers. A site that migrated off Bootstrap two years ago may still ship a stylesheet full of
.col-md-*classes. Detected, but not used. - 2Wrappers. Something built on a framework that's built on another framework will report both, and the interesting one is the outer layer.
- 3Page-specific stacks. A marketing site on WordPress and an app on React can live on the same domain. Check more than one URL.
- 4Deliberate obfuscation. Some sites strip headers and rename bundles. Absence of evidence isn't evidence here.
- 5Version numbers. Reported versions come from whatever the library exposes, which is often the major version only, and occasionally a lie.
Turning it on your own site
The most useful version of this exercise is running it against something you own. What you're looking for:
- Headers you didn't mean to send.
X-Powered-By: PHP/7.4tells an attacker exactly which exploit list to open. Turn it off. - Vendors nobody remembers adding. Every third-party script is weight, a privacy consideration, and a supply-chain risk.
- Duplicate tooling. Two analytics providers, two tag managers, a chat widget from a trial that ended.
- Outdated libraries loaded from a CDN. Pin the version, add subresource integrity, and check it against a vulnerability database.
WDT's Third-party Domain Report and Resource Integrity Checker cover the last two directly: one lists the external domains, the other checks whether external scripts carry an SRI hash. The security checks guide goes deeper on why that matters.
Once you know what a site is running, the follow-up questions are usually about what it's costing in performance and what it exposes.