Field guide

Stack ScannerAudit

Stack Detector Field Guide: Reading a Site's Tech Stack From One URL

tech stackstack detectionscoutingcompetitor analysisaudit
Senrok Team
BySenrok Team
Published

Knowing what is actually running on a page is the first thing a senior engineer does before scoping any work against that site. A migration plan, a security audit, a competitive teardown, a partner integration — every one of them starts with the same question: what is this thing built on?

The Stack Scanner is the fast answer to that question. This guide is the operator's notes: how the signature matching actually works, what HTML-only detection can and cannot see, and how to read the confidence scores without over-trusting them.

How the scanner works

The scanner fetches the URL through our CORS proxy, parses the HTML response, and pattern-matches the result against a hand-curated signature database. Each signature is a specific pattern that indicates a specific technology — a <meta name="generator"> value, a script tag with a known path, a class prefix, a CSS variable name, a <link> to a known CDN.

For each match, the scanner reports three things:

  1. The technology detected.
  2. A category — CMS, Framework, Analytics, Hosting / CDN, JavaScript library, Marketing, or Other.
  3. A confidence label — High, Medium, or Low.

The matching is deterministic. The same input produces the same output. There is no LLM in the loop, no fuzzy matching on token overlap, and no inference from indirect signals. If the pattern is in the signature database and the pattern is in the HTML, the match is reported. If not, it is not.

Confidence labels, honestly

Confidence is not a probability. It is a categorical label that means a specific thing:

  • High — two or more independent strong signals matched. A meta tag AND a script tag AND a CSS class all pointing to the same stack is High. You can act on this without verifying.
  • Medium — a single strong signal matched. The pattern is in the database and in the HTML, but no second signal confirms it. You can act on this, but it is worth a quick spot-check.
  • Low — a single weak signal matched. The pattern is common across multiple technologies (a class prefix, a generic script name) and does not uniquely identify the stack. Treat this as a hint, not a finding.

The Evidence row on each match shows the exact pattern that triggered the detection. Read it. If the evidence is <meta name="generator" content="WordPress 6.4.2">, that is a high-confidence detection. If the evidence is class="jsx-abcdef", the signal is weaker and the label is appropriately Low.

What the scanner can see

The scanner can see anything that lives in the HTML response — the static markup the server returns, including:

  • <meta> tags (generator, application-name, theme-color)
  • <link> tags (manifest, stylesheets, preconnects to known CDNs)
  • <script> tags (analytics, tag managers, framework bundles, third-party widgets)
  • CSS class names and inline styles
  • <html> attributes (data-* attributes, class on the root element)
  • Comments in the HTML (some tools self-identify in a build comment)
  • Inline JSON config blobs (Next.js, Nuxt, Gatsby all emit one)
  • Open Graph and Twitter Card meta tags

The scanner can also see content that lives in inline JSON config blobs — for example, Next.js emits a __NEXT_DATA__ script tag that often identifies the build version. A Gatsby site emits a ___gatsby global. These are HTML-visible, so they are scannable.

What the scanner cannot see

This is the part operators tend to over-trust. The scanner is HTML-only. It cannot see:

  • Response headers from the originServer, X-Powered-By, Set-Cookie, custom platform headers. The proxy makes the request, so the scanner sees the proxy's response headers, not the origin's. The web server, the language runtime, and the edge platform are usually invisible.
  • Client-side hydration — anything that is rendered by JavaScript after the initial HTML response. If a framework hydrates a SPA, the scanner sees the bootstrap HTML, not the rendered DOM.
  • Authenticated pages — the proxy makes an unauthenticated request. Pages behind a login wall will return the login form, not the page content.
  • Subresources that require auth or cookies — if the page is gated, the subresources will be too. The scanner will report the gate, not the content behind it.
  • API responses — anything fetched by the client-side app after hydration. The scanner sees the bootstrap fetch, not the subsequent XHRs.
  • The DOM after JavaScript runs — by extension, anything that only exists in the rendered DOM. SPA-specific runtime features (route handlers, hydration markers) are invisible.

The practical effect: for marketing sites, content sites, and traditional CMS-driven pages, the scanner is accurate. For SPAs and authenticated apps, the scanner sees the front door, not the house.

When to run it

Four moments where the scanner earns its keep:

  1. Before a migration scoping call — to confirm the stack is what you were told, and to surface anything the team forgot to mention.
  2. Before a security audit — to know which headers and runtime features you need to look for, and which CMS plugins are likely in scope.
  3. Before a partner integration — to confirm the other side's API is reachable from a browser, and to surface the third-party SDKs you might be stepping on.
  4. For competitive teardowns — to know which CMS, framework, and analytics stack a competitor is running, and to spot migration signals (a new framework version, a new analytics vendor).

Don't run it as a monitoring check. The scanner is a one-shot audit, not a continuous monitor. Stack changes are slow; one audit per quarter is plenty for most sites.

Reading the output

The result page has three sections: a category summary, a list of detected technologies, and per-match evidence. A few things to keep in mind:

  • A "no results" finding is itself a finding. It can mean the site is using a custom stack, or that the signatures just haven't been added yet. The signal here is "we couldn't fingerprint this", not "this site has no stack".
  • A High confidence on a CMS is actionable. A High on Cloudflare is not (Cloudflare is detected from headers; we see the proxy, not the origin, so the signal is unreliable). Filter for the categories that matter to your question.
  • The "Hosting / CDN" category is the noisiest because the scanner can only see the proxy, not the origin's edge. Treat that category as "the proxy is hosted on X", not "the site is hosted on X".
  • Marketing tags and analytics are the most accurate because they live in the HTML by design. If the scanner reports HubSpot or Segment, that is reliable.

Common patterns and what they tell you

A few detection patterns that come up over and over:

  • Next.js + Vercel + Segment + HubSpot — modern B2B SaaS marketing site, server-rendered with an edge platform. The __NEXT_DATA__ blob is the smoking gun.
  • WordPress + Yoast + MonsterInsights — content site with a long history. The wp-content paths and the yoast JSON-LD block are the fingerprints.
  • Shopify + Klaviyo + Google Analytics — DTC commerce site. The cdn.shopify.com script and the shopify-features body class are the tell.
  • Astro + Cloudflare + Plausible — modern static-first site on an edge platform. The astro- data attributes and the /_astro/ script paths are the fingerprint.
  • Webflow + Mixpanel — design-led site with product analytics. The webflow- data attributes and the mixpanel.init script are the tell.

These are patterns, not rules. The scanner reports the technologies; you make the call about what they imply.

When to graduate to a custom system

If you are running the scanner more than a few times a week, you are probably at the point where a custom system makes more sense. A few signals that it is time:

  • You need to audit pages behind authentication, which the proxy cannot do.
  • You need to see the response headers from the origin, which the proxy hides.
  • You need to audit SPA pages after hydration, which requires a headless browser, not an HTML fetch.
  • You need to track stack changes over time, not just one-shot audits.
  • You need to detect custom internal tools that are not in the public signature database.

In any of those cases, the right move is a custom stack-detection system that runs against your own infrastructure, with the headers, authentication, and signals you actually need. The free scanner is the right starting point, not the final word.


The scanner is at /tools/stack-detector. Run it on a URL before any migration, audit, or integration conversation. The proxy makes one fetch per scan; nothing is stored, and nothing is sent to a third party beyond the URL you submit.