Skip to content We're live on Product Hunt today Upvote
install

notes 5 min builds

note

What a page tells you about how it was built

Every page announces its own construction, in six different ways, with six different levels of honesty. Knowing which kind of evidence you are looking at matters more than the list of names it produces.

Stack detection looks like pattern matching and mostly is. What separates a useful answer from a confident wrong one is whether the tool tells you how it knew — because the six kinds of evidence a page leaves are not equally reliable, and two of them are trivially faked.

The six, weakest last #

A DOM marker
An attribute or class the framework itself writes - data-reactroot and its successors, a hydration marker, a generated class prefix. Strong: it is a side effect of the thing running, not a declaration about it.
A page global
A variable the library leaves on window. Equally strong, and often carries a version the page did not mean to publish.
A namespaced CSS variable
A custom property whose prefix belongs to one design system. Quietly one of the best signals, because it survives the minification that erases the others.
An asset URL
A script or stylesheet whose path names the thing. Good, with a caveat: a bundled copy under a hashed name says nothing, so absence here is not evidence of absence anywhere.
A response header
The server naming its own software. Reliable when present, and routinely removed, rewritten or set to something untrue as a matter of policy.
The generator meta tag
A declaration by the page about itself. The weakest of the six: a string somebody typed, surviving long after the thing it names was replaced, and the first thing edited by anyone who does not want to be identified.
DOM marker
page global
CSS variable prefix
asset URL
response header
generator meta tag

a side effect of the thing running a string somebody typed

The same six kinds, weighted by how hard each is to fake. The top three are side effects of code running; the bottom one is a sentence in the markup.

The ordering is the useful part. Two tools can report the same name from the top of that list and from the bottom of it, and those are not the same finding.

the same claim, at both ends of the list
<meta name="generator" content="WordPress 5.2">

<link rel="stylesheet"
      href="/wp-content/themes/twentytwenty/style.css?ver=6.5">

<!-- The first is a string in a template, unchanged since 2019.
     The second is a file the server is really serving,
     and the two disagree by four major versions. -->

Why the evidence has to be shown #

A flat list of names is not checkable. Told that a page is running a particular framework, you have no way to know whether that came from a hydration marker or from a meta tag somebody left in a template five years ago — and those two facts deserve different amounts of belief.

So every match here comes with the literal thing that matched and where it was found. It makes the readout longer and it is the difference between a claim and a finding.

It also makes the detector correctable. When a match is wrong, the evidence says which rule to fix rather than leaving somebody to guess at a heuristic they cannot see.

Absence is not evidence #

The failure that produces confidently wrong readouts is not a bad match. It is treating a missing signal as a negative finding — and on a modern build, missing is the normal case.

Bundling
A library compiled into main.a1b2c3.js leaves no filename to match. It is running; the URL says nothing.
Minification
Class prefixes and global names are renamed. A framework can be present with every one of its usual markers gone.
Server rendering
Markup arrives fully formed. The runtime that produced it may never have reached the browser at all.
A stripped header
Removed as a matter of policy at more or less every serious host. Its absence is a security setting, not a fact about the stack.

So the honest output has two states rather than three: identified, with the evidence and not identified. There is no not present, and a tool offering one is telling you something the page did not say.

Versions, and what a version is worth #

Where a page states a version, it is read and reported. Where it does not, nothing is invented — a guessed version is worse than a missing one, because the missing one prompts a question and the guessed one ends it.

Some versions cannot be told apart from the outside at all. Two major releases of the same tool can declare identical variable names, and a detector claiming to distinguish them is claiming something the page did not say.

Detecting yourself is the useful direction #

The instinct is to point this at somebody else's site. The finding that changes a decision is usually on your own, because a stack list from the outside is a list of what actually reached a browser — not what the lockfile says, not what the bundler was configured to split, not what a colleague removed six months ago.

  • A library everyone believes was dropped, still shipping in a chunk nothing imports any more.
  • Two versions of the same framework, because a widget brought its own.
  • A tag manager loading a second analytics vendor that no one on the team chose.
  • A consent banner that itself loads three third parties before consent is given, which is the most common finding of all and the most expensive one.

That last one is worth checking on any site with a cookie banner. It is a compliance failure that is invisible from the inside and takes about four seconds to see from the outside.

What to do with the answer #

The immediate use is orientation: opening an unfamiliar codebase's output before opening its source tells you what you are about to read.

The slower use is the third-party list. Most pages carry more analytics, tag managers, session replay and consent tooling than anybody on the team believes, because each was added once by somebody with a good reason. Seeing them named together, with the evidence for each, is usually the most uncomfortable part of the readout — and the most useful.

Written by Ján Turský

Building LoupeKit and other browser tools out of Bratislava, under Apptiary.

tools in this note