dotvitals identifies the software behind a site from141 fingerprints it wrote itself. Each one records the primary source it was written from, and this page is generated from those records — not from a summary of them — so you can check any single claim rather than take the promise on trust.
What a fingerprint is
A fingerprint is one observable thing in an HTTP response, plus the technology that thing implies, plus how much a match of it alone is worth. Nothing more. There is no JavaScript execution, no port scan and no crawl behind a technology result: one page is fetched, and the response is compared against the corpus below.
The kinds of signal a fingerprint can read, and how many read each
Signal
Fingerprints
What it reads
Response header value
41
A named header whose value matches a pattern. Often carries a version.
Response header present
25
The mere presence of a header only one vendor emits. No value is read.
Cookie name
10
The name of a cookie the site sets. The value is never inspected.
Generator meta tag
18
The content of <meta name="generator">, which is the site declaring itself.
Page body string
47
A string in the HTML, such as a script host. The weakest kind: a tutorial can quote it. Never reaches confirmed.
Cookie names are read; cookie values never are. A detection is always a read of what the site chose to disclose, never an inventory of what it runs, and a result that finds nothing is not a clean bill of health — a CDN that rewrites headers hides real technologies.
What we refuse to do
dotvitals does not use a third-party fingerprint dataset, and never has. Not as a source, not as a starting point, not as a reference to check a name against.
Fingerprint datasets dotvitals does not consult
Dataset
Licence
Wappalyzer (the original dataset and its forks)
GPL-3.0
enthec/webappanalyzer
GPL-3.0
The HTTP Archive Wappalyzer fork
GPL-3.0
WhatWeb
GPL-2.0
wappalyzergo
MIT wrapper over a GPL dataset
BuiltWith
proprietary, licensed per seat
The GPL ones are the reason the rule is written as "do not consult" rather than "do not copy". Rewriting or reformatting a GPL dataset still produces a derivative work of it: changing the form does not change the origin. A corpus assembled by paraphrasing someone else's regular expressions is that dataset wearing different punctuation, whatever the commit history says.
Writing our own is slower and the corpus is smaller than a mature dataset's. What it buys is that every detection can say which signal it came from and why that signal is trusted at the level it is — which is the whole content of this page, and is not something an imported list could support.
What provenance means here
Provenance is a required field of the fingerprint itself, not a document kept alongside it. A parallel document drifts; a required field cannot be omitted. Each record names a primary source — one of three things, and nothing else.
Primary source types, and how many fingerprints rest on each
Source type
Fingerprints
What it means
Vendor documentation
55
The company that makes the product says in its own public documentation that the product emits this signal. The strongest source available, because it is the vendor describing its own behaviour.
Standards document
0
An RFC or a W3C/WHATWG specification defines the signal. Used where the signal is a standard mechanism rather than a product's own marker.
Direct observation
86
We fetched a named site on a named date and recorded the exact bytes it sent. Reproducible by anyone, and never enough on its own to reach confirmed.
Every record carries the source URL, the day it was read, and what that source states. A direct observation additionally carries the exact bytes we saw, so a reviewer can reproduce the finding instead of taking our word for it. All86of our 86 observations do.
A fingerprint without complete provenance fails the build. Not a review habit — a test. packages/technology/test/provenance.test.ts rejects a missing or non-HTTPS source URL, a missing or unparseable date, a placeholder description of what the source says, and an observation with no recorded bytes. It runs on every commit, and the reason it exists is that the only moment this rule is ever tested is the moment somebody adds a fingerprint in a hurry.
All 141 of the 141fingerprints shipping today carry a complete record. The same data generatesdocs/30-technology-fingerprint-provenance.md in the repository, and a second test fails if that file and this corpus disagree.
How confidence is priced
A detection is an inference from bytes a server chose to send, and some inferences are far weaker than others. The confidence attached to a fingerprint records what a match ofthat signal alone is worth, and it is never promoted: two low signals stay low.
Confidence levels and how many fingerprints hold each
Confidence
Fingerprints
What it means
confirmed
15
The vendor documents the signal and the signal names the product. No inference is involved.
high
97
A marker only one vendor emits, but which does not name the product.
medium
24
A marker the product emits which others plausibly emit too.
low
5
A build artefact or a widely copied convention. A hint, not an identification.
Two limits on confirmed follow from that, and both are counted off the corpus rather than promised:
An observation alone is never confirmed. A signal we saw on one live site is an inference by definition, however convincing it looked. Fingerprints that are confirmed on an observation alone:0.
A page-body match is never confirmed. A string in an HTML document is copyable, cacheable and frequently pasted into unrelated templates — a changelog can quote a vendor's script host without the site using the product. Confirmed body matches:0.
Both numbers are zero because the build refuses to let them be anything else. They are printed rather than asserted so you can check them against the table below, where every fingerprint's confidence, signal kind and source type are shown side by side.
The corpus: 141 fingerprints
Fingerprints by technology category
Category
Fingerprints
Web servers
8
Reverse proxies
4
CDNs
15
Web application firewalls
7
Content management systems
23
Ecommerce platforms
11
JavaScript frameworks and site generators
26
Analytics
14
Tag managers
4
Language runtimes
12
Hosting platforms
17
Every fingerprint, with its evidence
One row per fingerprint: what we look at, what a match is worth, and the source that entitles us to say so. Open a category to read it. This is the part of the page that makes the rest checkable — if a claim above is wrong, it is wrong here first.
Analytics (14)
Adobe Analytics adobe-analytics-appmeasurement
Signal
page body matching \bAppMeasurement\.js
Confidence
high — AppMeasurement.js is the library Adobe documents for sending data to Adobe Analytics.
The implementation overview states that the AppMeasurement.js code "contains the libraries required to send data to Adobe" and should be added to the site template's head, with s.t() used to send the data.
Amplitude amplitude-cdn-script
Signal
page body matching cdn\.amplitude\.com\/script\/
Confidence
high — The browser SDK is loaded from Amplitude's own CDN host.
The docs give `<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js" data-cf-beacon='{"token": "...", "spa": false}'></script>` and the equivalent `?token=…&spa=false` query-string form.
Fathom Analytics fathom-analytics-script
Signal
page body matching cdn\.usefathom\.com\/script\.js
Confidence
high — The script is loaded from Fathom's own CDN host, exactly as Fathom documents it.
The install page gives `<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID"></script>` followed by `gtag('config', 'TAG_ID')`, where TAG_ID is the Google tag identifier issued by products including Analytics.
Matomo matomo-paq-global
Signal
page body matching window\._paq\s{0,4}=
Confidence
high — Matomo's documented tracking code queues commands on a window._paq array.
The install snippet calls posthog.init with `api_host: 'https://us.i.posthog.com'`, and the loader rewrites that host to `-assets.i.posthog.com` before appending /static/array.js.
PostHog posthog-init-call
Signal
page body matching posthog\.init\s{0,4}\(
Confidence
high — PostHog's documented snippet bootstraps the SDK with a posthog.init call.
The loader documentation gives `<script src="https://js.sentry-cdn.com/<your-public-key>.min.js" crossorigin="anonymous"></script>` and refers to the CDN bundles hosted at https://browser.sentry-cdn.com/.
CDNs (15)
Akamai akamai-ghost-server-header
Signal
response header server: ^AkamaiGHost
Confidence
high — AkamaiGHost is the Server value Akamai's edge ghost servers return.
An Akamai-delivered production site answers with a Server header naming Akamai's edge software. Several other large sites fetched the same day returned the identical value.
Bytes observed
server: AkamaiGHost
Akamai akamai-grn-header
Signal
presence of the response header akamai-grn
Confidence
high — Akamai-GRN carries Akamai's Global Request Number for a request.
The Global Request Number behaviour 'generates a unique identifier for each request on the Akamai edge network, for use in logging and debugging', in a format such as 0.05313217.1567801841.1457a3, and can be output as a response header named Akamai-GRN.
Akamai NetStorage akamai-netstorage-server-header
Signal
response header server: ^AkamaiNetStorage
Confidence
high — Akamai's NetStorage origin names itself in the Server header.
'Via – CloudFront sets the value to the following in the response to the viewer: Via: {{http-version}} {{alphanumeric-string}}.cloudfront.net (CloudFront)', with the example 'Via: 1.1 1026589cc7887e7a0dc7827b4example.cloudfront.net (CloudFront)'.
Amazon CloudFront cloudfront-x-amz-cf-id
Signal
presence of the response header x-amz-cf-id
Confidence
high — X-Amz-Cf-Id is CloudFront's per-request identifier.
A CloudFront-fronted host returns X-Amz-Cf-Id on the viewer response. AWS documents the same header on the request it forwards to the origin, describing it as 'an encrypted string that uniquely identifies the request'.
Under 'From the Front Door to the client': 'Front Door also attaches the following headers to all responses to the client' — X-Azure-Ref, 'a unique reference string that identifies a request served by Front Door, which is critical for troubleshooting as it's used to search access logs.'
bunny.net bunnycdn-pullzone-header
Signal
presence of the response header cdn-pullzone
Confidence
high — cdn-pullzone identifies the bunny.net pull zone that served the response.
The response carries a family of CDN-* headers — cdn-pullzone, cdn-requestid, cdn-cache, cdn-edgestorageid — of which cdn-pullzone names the bunny.net pull zone id.
Bytes observed
cdn-pullzone: 876725
bunny.net bunnycdn-server-header
Signal
response header server: ^BunnyCDN
Confidence
high — bunny.net edge nodes name the product and the node in the Server header.
The page lists Cf-Cache-Status among the headers Cloudflare adds, noting that 'A list of all possible `Cf-Cache-Status` values is contained in Cloudflare cache responses.'
Cloudflare cloudflare-cf-ray
Signal
presence of the response header cf-ray
Confidence
high — Cf-Ray is Cloudflare's per-request identifier and is emitted by no one else.
Cloudflare's own site, and every other Cloudflare-proxied host fetched on the same day, answered with a lower-case Server header containing exactly the product name.
Fastly sets this header on all responses it processes; it 'contains the identity of the cache server acting as the delivery node', in the form cache-{datacenter}{nodeid}-{datacenter}, and may list several entries separated by commas when shielding is in use.
The sendPoweredByHeader general config setting is documented as enabled by default and causes Craft to send an X-Powered-By header identifying Craft CMS; it can be disabled via config or the CRAFT_SEND_POWERED_BY_HEADER environment variable.
wp_generator() "Displays the XHTML generator that is generated on the wp_head hook", outputs a generator meta tag containing the WordPress version, and is removed with remove_action( 'wp_head', 'wp_generator' ).
WordPress wordpress-wp-content-path
Signal
page body matching \/wp-content\/(?:themes|plugins|uploads)\/
Confidence
medium — Asset URLs under the default wp-content layout that WordPress ships with.
Plugins live in wp-content/plugins, themes in wp-content/themes and uploads in wp-content/uploads by default, but the handbook warns that users may place the wp-content directory anywhere and rename it, so the location must never be assumed.
Ecommerce platforms (11)
BigCommerce bigcommerce-cdn-body
Signal
page body matching \/\/cdn[0-9]{1,2}\.bigcommerce\.com\/
Confidence
high — BigCommerce Stencil storefronts serve theme assets and product images from cdn*.bigcommerce.com.
BigCommerce's CDN and images documentation gives storefront image URLs in the form https://cdn11.bigcommerce.com/s-<store hash>/images/stencil/<size>/products/... and describes the {{cdn}} helper that produces them.
Ecwid ecwid-storefront-script
Signal
page body matching app\.ecwid\.com\/script\.js
Confidence
high — The Ecwid storefront widget is installed by loading app.ecwid.com/script.js with the store id.
Ecwid's installation instructions tell merchants to paste a snippet loading https://app.ecwid.com/script.js?STOREID&data_platform=code together with a my-store-STOREID container div.
Adobe's Varnish verification page lists the important response headers as X-Magento-Cache-Control, Age and "X-Magento-Cache-Debug: MISS", noting that "X-Magento-Cache-Debug: HIT" is also acceptable.
The URLUtils Script API reference states that url(), http(), https() and abs() generate a Commerce Cloud Digital URL of the form "/on/demandware.store/..." by default, and Salesforce's SFRA guide gives the example https://localhost/on/demandware.store/Sites-RefArch-Site/default/Hello-World.
Shopify shopify-cdn-body
Signal
page body matching \/\/cdn\.shopify\.com\/
Confidence
high — Shopify serves theme and storefront assets from its own cdn.shopify.com host.
Shopify's theme performance guidance describes the Shopify CDN and tells theme developers "You don't need to hard-code the cdn.shopify.com domain in your theme", confirming cdn.shopify.com as the platform's asset host.
Shopify shopify-essential-cookie
Signal
a cookie whose name matches ^_shopify_essential$
Confidence
high — Shopify storefronts set the _shopify_essential session cookie on the first response.
A production Shopify storefront inlines a script that sets Shopify.shop to the store's myshopify.com handle, and loads storefront modules from cdn.shopify.com/shopifycloud/.
Bytes observed
Shopify.shop = "weareallbirds.myshopify.com"
Shopware shopware-cache-hash-cookie
Signal
a cookie whose name matches ^sw-cache-hash$
Confidence
high — Shopware's storefront HTTP cache sets the sw-cache-hash cookie to key the cached response.
Shopware's HTTP cache documentation describes sw-cache-hash as containing "the hash of all cache-relevant information (e.g. is the user logged-in, what tax state and what currency do they use, which cache-relevant rules have matched)".
Shopware shopware-states-cookie
Signal
a cookie whose name matches ^sw-states$
Confidence
high — Shopware sets the sw-states cookie to describe the session for cache invalidation.
The same page states that sw-states "describes the current session in simple tags like cart-filled and logged-in" and is matched against the sw-invalidation-states response header; it also notes the cookie is deprecated and will be removed in v6.8.0.0.
Both the S3 REST endpoint and the s3-website-us-east-1.amazonaws.com static hosting endpoint answered with a Server header containing exactly this value, next to x-amz-request-id.
Bytes observed
Server: AmazonS3
Fly.io fly-io-request-id
Signal
presence of the response header fly-request-id
Confidence
high — fly-request-id is the identifier Fly.io's proxy attaches to each response.
The Pages response carries a GitHub request identifier. GitHub's API and web frontends emit the same header, so this signal narrows the host to GitHub rather than to Pages specifically.
Requests to the Cloud Storage endpoint answered with this Server token; the same token appeared on a Google-hosted response carrying an x-guploader-uploadid header.
Bytes observed
server: UploadServer
Heroku heroku-spaces-router-via
Signal
response header via: \bspaces-router\b
Confidence
medium — Heroku's Private Spaces router inserts itself into the Via chain.
Render's own site returns an rndr-id header together with x-render-origin-server: Render, which is what ties the abbreviated header name to the vendor.
Bytes observed
rndr-id: 5b169814-e841-4a42
Vercel vercel-server-header
Signal
response header server: ^Vercel$
Confidence
confirmed — Vercel documents that its deployments answer with server: Vercel.
'The `x-vercel-cache` header indicates the cache status of static assets and responses from Vercel's CDN', with the documented values HIT, MISS, STALE, PRERENDER, REVALIDATED and BYPASS.
Vercel vercel-x-vercel-id
Signal
presence of the response header x-vercel-id
Confidence
high — x-vercel-id records the Vercel regions a request passed through.
The installation page gives `<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>` and advises hardcoding a version in production.
Alpine.js alpinejs-x-data-attribute
Signal
page body matching \sx-data\s{0,4}=
Confidence
medium — Alpine.js requires an x-data attribute to initialise any component.
"generator provides the current version of Astro your project is running. This is a convenient way to add a <meta name="generator"> tag with your current version of Astro. It follows the format "Astro v5.x.x"." The docs show `<meta name="generator" content={Astro.generator} />`.
Astro astro-scoped-style-attribute
Signal
page body matching \sdata-astro-cid-[0-9a-z]{4,16}
Confidence
low — Astro's scoped styles add a data-astro-cid-* attribute to component elements.
"The fastest way to get going with htmx is to load it via a CDN. You can simply add this to your head tag and get going: <script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.10/dist/htmx.min.js">".
htmx htmx-hx-attributes
Signal
page body matching \shx-(?:get|post|put|patch|delete|boost|swap|target|trigger)\s{0,4}=
Confidence
high — htmx drives requests entirely from its documented hx-* attribute namespace.
The docs define hx-get, which "issues a GET request to the given URL", and hx-post, which "issues a POST request to the given URL", as the core AJAX attributes.
hugo.Generator "Renders an HTML meta element identifying the software that generated the site", with the example output `<meta name="generator" content="Hugo 0.166.0">`.
The official release listing publishes CDN URLs of the form https://code.jquery.com/jquery-3.7.1.min.js and https://code.jquery.com/jquery-3.7.1.slim.min.js.
jQuery jquery-script-filename
Signal
page body matching \bjquery(?:-(\d{1,2}(?:\.\d{1,3}){0,2}))?(?:\.slim)?(?:\.min)?\.js
Confidence
low — A script named jquery*.js is loaded, which is the conventional filename for the library.
The official release listing names the distributed files jquery-<version>.js, jquery-<version>.min.js and jquery-<version>.slim.min.js; those filenames are commonly self-hosted.
Next.js nextjs-flight-payload-global
Signal
page body matching __next_f\b
Confidence
medium — The App Router streams its server payload into a __next_f buffer.
"Next.js will automatically use your asset prefix for the JavaScript and CSS files it loads from the /_next/ path (.next/static/ folder)", with the example request path /_next/static/chunks/….js
Nuxt nuxt-build-asset-path
Signal
page body matching \/_nuxt\/
Confidence
low — Nuxt's default build directory is served from /_nuxt/.
The link options page documents `data-sveltekit-preload-data` with the values "hover" ("preloading will start if the mouse comes to a rest over a link") and "tap".
Vue.js vue-scoped-style-attribute
Signal
page body matching \sdata-v-[0-9a-f]{6,10}\b
Confidence
low — Vue's scoped single-file-component styles add a data-v-* attribute to elements.
The SFC CSS features page shows `<style scoped>` transformed by PostCSS so that `<div class="example">hi</div>` becomes `<div class="example" data-v-f3f3eg9>hi</div>`.
Language runtimes (12)
ASP.NET aspnet-powered-by
Signal
response header x-powered-by: ^ASP\.NET$
Confidence
high — The server names ASP.NET in its X-Powered-By header.
The IIS project site returns the ASP.NET powered-by header alongside X-AspNet-Version and X-AspNetMvc-Version. The same header was observed on https://dotnet.microsoft.com/ on the same date.
Bytes observed
x-powered-by: ASP.NET
ASP.NET aspnet-session-cookie
Signal
a cookie whose name matches ^ASP\.NET_SessionId$
Confidence
confirmed — ASP.NET_SessionId is the documented default session cookie name and names the framework.
SessionStateSection.CookieName is declared `[ConfigurationProperty("cookieName", DefaultValue="ASP.NET_SessionId")]`, and the Remarks state "The default value is \"ASP.NET_SessionId\"".
"By default, Express sends the X-Powered-By response header that you can disable using the app.disable() method", shown as `app.disable('x-powered-by')`.
sessionCookieName is "The name to be used for all session cookies created for this context… If not set, the value specified by the web application, if any, will be used, or the name JSESSIONID if the web application does not explicitly set one."
Laravel laravel-xsrf-token-cookie
Signal
a cookie whose name matches ^XSRF-TOKEN$
Confidence
medium — Laravel sets an encrypted XSRF-TOKEN cookie on every response it generates.
"Laravel stores the current CSRF token in an encrypted XSRF-TOKEN cookie that is included with each response generated by the framework… some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the X-XSRF-TOKEN header."
Perl perl-server-header
Signal
response header server: \bPerl\b
Confidence
high — The server names Perl in its Server header.
expose_php "Exposes to the world that PHP is installed on the server, which includes the PHP version within the HTTP header (e.g., X-Powered-By: PHP/5.3.7)", with a documented default of "1".
PHP php-session-cookie
Signal
a cookie whose name matches ^PHPSESSID$
Confidence
high — PHPSESSID is PHP's documented default session cookie name.
'Using disable headers, you can configure your Application Load Balancer to disable the `server:awselb/2.0` header from the responses.' The controlling attribute is routing.http.response.server.enabled, whose default value is true.
Envoy envoy-server-header
Signal
response header server: ^envoy$
Confidence
confirmed — Envoy's HTTP connection manager writes 'envoy' to the Server header by default.
A production site fronted by Varnish overrides the Server header with the product name. This is a deployment choice rather than a Varnish default, which is why it is only medium.
The Cloudflare home page loads a Google Tag Manager container. Google's public documentation for the container snippet is behind an account flow, so this entry rests on observation; the host and the GTM- container-ID prefix both name the product.
Bytes observed
www.googletagmanager.com/gtm.js?id=GTM-NDGPDFZ
Google Tag Manager google-tag-manager-noscript-iframe
Signal
page body matching www\.googletagmanager\.com\/ns\.html\?id=GTM-[A-Z0-9]{4,12}
Confidence
high — The noscript iframe of the Google Tag Manager container snippet, carrying a literal GTM container id on a host only Google serves.
The served HTML contains the Google Tag Manager noscript fallback iframe with a literal GTM container id, while the script-tag half of the same snippet has its id injected at runtime and so carries none.
Sucuri's own site is served through its firewall and returns a cache-outcome header carrying the product name, next to x-sucuri-id and a Sucuri Server header.
Bytes observed
x-sucuri-cache: HIT
Sucuri Website Firewall sucuri-x-sucuri-id
Signal
presence of the response header x-sucuri-id
Confidence
high — X-Sucuri-ID identifies the Sucuri edge node that answered the request.
ServerTokens 'Determines what the server returns in the Server HTTP response header', with example values 'Server: Apache' (Prod), 'Server: Apache/2.4' (Minor), 'Server: Apache/2.4.41 (Ubuntu)' (OS) and the default Full 'Server: Apache/2.4.41 (Ubuntu) OpenSSL/1.1.1 (some modules)'.
Caddy caddy-server-header
Signal
response header server: ^Caddy$
Confidence
high — Caddy identifies itself by name in the Server header.
The vendor's own site answers with a Server header naming LiteSpeed. openlitespeed.org, fetched the same day, sends the identical value, so the header does not distinguish the open-source build from the commercial one.
'IIS 10.0 added the removeServerHeader attribute to suppress sending the HTTP server header to remote clients.' The attribute table adds: 'If set to true, request filtering will suppress the IIS server header. If set to false, IIS will return the default server header.'
The server_tokens directive 'Enables or disables emitting nginx version on error pages and in the "Server" response header field.' Its default is `server_tokens on;`, and an empty string value 'disables the emission of the "Server" field.'
Where this fits
Technology detection is reported, never scored: identifying a product is not a fault, and the check deducts nothing. How the rest of a report is scored is published the same way, on the scoring methodology page, and the check itself lives atTechnology Lookup.