Tech Stack Detector: Website CMS, Framework & Hosting Lookup
Pricing
from $2.00 / 1,000 website analyseds
Tech Stack Detector: Website CMS, Framework & Hosting Lookup
Tech stack detector for websites: finds the CMS, ecommerce platform, JavaScript framework, analytics, CDN and hosting a site uses, with evidence for each finding. One polite HTTP request per URL. Accepts URLs or bare domains; works from the API and AI agents (MCP). Honours robots.txt.
Pricing
from $2.00 / 1,000 website analyseds
Rating
0.0
(0)
Developer
Jack Valmadre
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
a month ago
Last modified
Categories
Security
Limited permissionsShare
Website Tech Stack Detector
Give it a list of website URLs; get back technology markers found on each page: CMS, ecommerce platform, JavaScript frameworks and libraries, analytics and tag managers, CDN, hosting, web server, payment and chat widgets, and more, with the evidence for each finding.
What it does
For each URL you give, the actor makes one ordinary HTTP request to that page (after reading the site's robots.txt once; redirects are followed) and looks at:
- response headers (for example
Server,X-Powered-By, CDN headers) - cookie names set by the page (values are discarded immediately and never stored)
<meta name="generator">and similar meta tags<script src>and<link href>URLs- characteristic patterns in the HTML
It matches these against 396 signatures written by AI agents and reports each technology with its category, version (when the page states it) and what matched.
For sites that load Google Tag Manager, the actor fetches up to two public container scripts (one extra request per container, default on; readTagManager input) and identifies analytics and advertising tags configured for a plain page view, without running JavaScript. This does not prove that a browser delivered or executed those tags. For CDN and hosting detection, it also looks up the CNAME chain of the page host and asset subdomains (a few DNS queries, default on; useDnsHints input).
Capabilities summary
Filter website results by the capabilities detected on each page, without reading every technology name. Each analysed row includes a capabilities object covering:
| Field | Detected capability |
|---|---|
analytics | Web analytics |
adPixel | Advertising conversion pixels |
liveChat | Live chat |
onlineBooking | Online booking |
ecommerce | Online store or payments |
emailMarketing | Email marketing or marketing automation |
cookieConsent | Cookie consent banner |
A value of true means the actor found a matching technology or page marker. null means it did not detect the capability and its presence is unknown. notDetected lists those unknown capability keys. capabilityTools names the technologies or page markers behind each detected capability, such as page: link to a booking page. A matching link or script does not establish that a booking flow, payment or other feature works.
The summary adds no requests beyond the detection described above. On blocked, failed, robots-disallowed and skipped URLs, all three summary fields are null because the page was not analysed.
Limits: An unknown value does not establish absence. Tools can load after consent, appear only on another page, use an unsupported vendor or be built in-house. The summary describes detected evidence on the analysed page.
What it does not do
- No browser. JavaScript is not executed, so technologies that are only loaded later by scripts, or only appear after login or consent, are not seen.
- No bypassing of blocks. If a site answers with a recognised bot challenge or refusal (HTTP 401, 403, 429 or 503, a recognised challenge page, or Cloudflare's challenge header), the URL is reported as
blocked(with any facts visible in the response headers, e.g. the CDN) and is not retried through proxies or other tricks. Blocked URLs are not charged (see Pricing). Known passive Cloudflare JavaScript-detection scripts alone do not make a public page a challenge. An unfamiliar challenge page served with HTTP 200 and no challenge header may not be recognised; it is then counted as analysed (and charged), usually with few or no technologies found. - robots.txt is honoured by default, read the standard way (RFC 9309:
*and$wildcards, and the most specific matching rule wins), including for the target of a redirect (a redirect to another site is checked against that site'srobots.txt). Disallowed pages are reported asdisallowed-by-robotsand not fetched. ACrawl-delayinrobots.txtis respected between requests to the same site; a site that asks for more than 10 seconds between requests is reported asdisallowed-by-robotsand not fetched. You can switch robots.txt handling off only for sites you own or have permission to test. - No crawling. Only the URLs you list (and any redirects they answer with) are fetched, at most one request at a time per host.
- It is not a security scanner and does not probe for vulnerabilities.
- Detection is passive and can miss technologies that leave no trace in the first HTML response (for example a Laravel or Django site whose home page sets no framework cookie and has no form). It can also be fooled by sites that deliberately mimic or hide markers.
Offline accuracy report (October 2026)
Offline comparison on 227 fresh held-out websites, captured 3 October 2026. Both methods use the same visits and reviewed reference labels, with different inputs. These results describe the source replays below; they do not measure the current Apify build, its default configuration or Wappalyzer's commercial service.
In the table, Madrasco means the main-response detector-source replay. Open means the Wappalyzer-derived open-rules replay using saved browser and infrastructure observations.
Precision is the share of reported technology labels supported by the reviewed reference. Recall is the share of supported reference labels each method found. A website can contribute several labels: these figures are not the percentage of websites whose entire technology stack was correctly identified.
| Category | Scored sites | Labels | Madrasco precision | Madrasco recall | Open precision | Open recall |
|---|---|---|---|---|---|---|
| E-commerce platform | 225 | 19 | 19/29 | 19/19 | 39% | 18/19 |
| CMS | 218 | 138 | 99% | 95% | 71% | 98% |
| Analytics / marketing | 181 | 138 | 96% | 65% | 46% | 65% |
| Frontend framework / UI library | 203 | 622 | 98% | 34% | 83% | 83% |
| Hosting | 123 | 125 | 85% | 48% | 76% | 59% |
| CDN | 192 | 135 | 95% | 54% | 62% | 41% |
Percentages are rounded to whole numbers. Fractions show matched labels / reported labels for precision, and found labels / reference labels for recall. E-commerce has only 19 reference labels: Madrasco precision is 19/29, separately from recall 19/19; Open precision is 18/46 (39%) and recall is 18/19. These small counts do not establish market-wide e-commerce accuracy. N/A would mean no denominator.
Scored sites includes resolved assessments with no observed technology in that category. Labels counts positive website–technology/category pairs after the shared scope exclusions. The same technology on two websites contributes two labels. The category denominators differ because unresolved assessments are excluded equally for both methods. The hosting reference could be resolved for 123 of 227 websites, with 125 attributable provider labels. This describes reference coverage, rather than the number of hosts found by either detector. An unresolved provider does not establish that a website has no host.
The sample is the completed, permission-screened, technology-enriched subset of a stratified candidate draw. It is not category-balanced or representative of all websites. Across the six categories, 220 unresolved site/category assessments are excluded in full for both methods. They contain 129 supported labels in 55 cells, retained separately outside scoring. Other resolved categories on those same sites can still contribute results. Failed, blocked or missing tool output counts as missed positive labels where the reference assessment is resolved.
Inputs supplied to each replay
Madrasco source replay (main response) receives the final URL, HTTP status, main-response headers including Set-Cookie, and raw response HTML. That HTML can contain metadata, script URLs, inline code and page text. This replay supplies no DNS hints, GTM container fetches, browser cookie/global observations, rendered DOM or subresource bodies.
Open rules replay (browser signals) receives the main URL and headers; saved rendered HTML, accessible stylesheet text, metadata and script URLs; browser cookie observations; main-host DNS records and certificate issuer; saved JavaScript-global and DOM observations; and received external script bodies and observed XHR hosts. The saved HTML includes inline markup. There are no separate joined inline-script or page-text channels. It uses pinned Enthec webappanalyzer rules with the pinned HTTP Archive engine and browser helpers. A custom offline driver supplies saved evidence to those dependencies; it does not run the upstream crawler, a default buyer build or a commercial Wappalyzer service.
Both replays run offline. The broader Open inputs can expose evidence absent from the main response; shared sites and labels do not make the input scopes equivalent.
What counts in each category
Frontend covers independently identifiable JavaScript frameworks, static site generators and DOM/UI rendering or component libraries. A delivered runtime inside an embedded widget counts, without establishing the main site's architecture or proving that every component executed. Styling toolkits, CSS-in-JS styling engines, generic utilities, positioning helpers and backend frameworks are excluded. Bootstrap (including plugins), Tailwind, Foundation and Bulma are excluded. A jQuery UI core/mouse helper alone does not establish a delivered native UI component.
Analytics / marketing covers visitor and event tracking, conversion/remarketing tags, tag managers and A/B testing. Generic forms, chat, email tools, ad serving and performance-only telemetry are outside this category. A configured snippet or queue, a consent catalog entry, or an installed tag that never loads during the visit does not establish observed runtime delivery.
CDN covers successful delivery of the site's page, content, assets or public-library files. A third-party service's own SDK, widget or API delivery does not establish the site's CDN. Hosting covers attributable providers serving the captured document, rather than nameservers, generic certificate issuers or unrelated asset/service hosts. CMS and e-commerce labels identify supported platform presence; generic builder capabilities or unused app catalogs do not establish an activated storefront.
The observation is one anonymous homepage visit. Technologies requiring login, consent choices or later interactions may be outside its coverage. A successfully completed 2xx or 304 response with no recorded failure supports delivery; an attempted, failed or unfinished request does not. The reference describes observed delivery, not everything installed on a company's websites.
How the reference was reviewed
AI agents first labelled captured evidence without detector predictions and preserved the initial reference freeze. After the replays, independent AI agents reviewed the name/category mappings and every disagreement against the saved captures. They made supported corrections, including missed native libraries, and preserved each earlier truth version. Machine validators checked exact evidence and file bindings. The displayed table uses final reference version 5. Neither Madrasco's detector signatures nor the open rules were tuned on these sites; the functional name/category mappings were reviewed separately and applied equally to truth and both methods.
Saved scoring records retain raw counts, completion rates, approximate label-level 95% Wilson intervals and hashes of the source, capture and review artifacts. Labels from one website can be correlated, so these intervals do not establish independent website-level uncertainty or estimate prevalence across the web.
Changes in this release
- Ordinary public pages containing known passive Cloudflare JavaScript-detection scripts can be analysed. Challenge headers, recognised access gates and other challenge code remain blocked.
- The accuracy section now reports the scoped October offline source comparison. It does not measure this release's default Apify configuration. No detection signatures were tuned on the comparison sites.
Input
| Field | Default | Meaning |
|---|---|---|
urls | - | Up to 1,000 URLs (bare domains are fetched over HTTPS). |
startUrls, domains, websites, url | - | Other names for the same thing, for API callers and AI agents: startUrls takes Apify's request-list format ([{"url": "..."}]) or plain strings, url takes one string. All given URL fields are merged and duplicates removed. |
respectRobotsTxt | true | Skip pages disallowed by robots.txt. |
timeoutSecs | 20 | Give up on a page after this many seconds. |
maxConcurrency | 10 | Different sites fetched in parallel (never more than one request at a time per host). |
includeEvidence | true | Include what matched for each technology. |
useDnsHints | true | Look up the CNAME chain of the page host and of the site's own asset subdomains (a few DNS queries) to identify CDNs and hosting platforms that don't always show in response headers (Akamai, Fastly, CloudFront, Netlify, Vercel, WP Engine…). Turn off to make the run cheaper and faster; you lose some CDN and hosting detections. |
readTagManager | true | If the page loads Google Tag Manager, fetch its public container script (one extra request per GTM container, up to two per page) and identify analytics and advertising tags configured for a plain page view. Tags held back for a consent banner are not reported. Turn off to save one or two requests per GTM-using page. |
Use with AI agents
The only input the actor needs is a list of websites. Any of the field names above works, so an agent that guesses startUrls or url still gets results; if no URL is given, the run fails with a message naming the accepted fields.
- Apify MCP server: add
https://mcp.apify.com?tools=madrasco/website-tech-stack-detectorto your MCP client (Claude, Cursor and others; see Apify's MCP docs). The agent then has a tool for this actor and calls it with, for example,{"urls": ["example.com", "https://shop.example.org"]}. - HTTP API, one call:
POST https://api.apify.com/v2/acts/madrasco~website-tech-stack-detector/run-sync-get-dataset-itemswith your Apify token in theAuthorization: Bearerheader and a JSON body such as{"urls": ["example.com"]}returns the dataset rows (one per URL) when the run finishes. For long lists, start a normal run and read the dataset afterwards.
Each row carries status, so an agent can tell "no technologies found" apart from blocked, disallowed-by-robots and error.
Output (one dataset row per URL)
Real output for https://wordpress.org/ (run by us on 2026-09-24), shortened: 3 of the 7 technologies found are shown, the long evidence list for WordPress is cut to two entries, and categories lists only the shown technologies.
{"url": "https://wordpress.org/","finalUrl": "https://wordpress.org/","httpStatus": 200,"status": "ok","error": null,"technologies": [{"name": "Nginx", "categories": ["Web servers", "Reverse proxies"], "version": null,"evidence": ["header: server: nginx"], "confidence": "direct"},{"name": "PHP", "categories": ["Programming languages"], "version": null,"evidence": ["implied by WordPress"], "confidence": "implied"},{"name": "WordPress", "categories": ["CMS", "Blogs"], "version": "7.2","evidence": ["meta: generator=WordPress 7.2-alpha-63914", "html: /wp-content/plugins/"],"confidence": "direct"}],"categories": {"Web servers": ["Nginx"], "Reverse proxies": ["Nginx"],"Programming languages": ["PHP"], "CMS": ["WordPress"], "Blogs": ["WordPress"]},"techCount": 7,"fetchedAt": "2026-09-24T14:40:15+00:00"}
status is one of ok, blocked, disallowed-by-robots, error (DNS failure, timeout, HTTP 4xx/5xx), or skipped (your run's maximum charge was reached before this URL was processed; not charged, and every URL you give still gets a row). confidence is direct (a marker matched) or implied (inferred from another technology, e.g. WordPress implies PHP).
Shopify theme and app detection
For websites running on Shopify, the actor also reports:
-
Theme name (
shopifyTheme): the theme's schema name (for example "Dawn", "Debut", "Impact") and the site's customized name, when the page declares it inShopify.theme. This isnullfor non-Shopify pages and for headless Shopify stores that don't expose the theme object. -
Installed apps (
shopifyApps): a list of Shopify apps visible in the home page's HTML, with their category (for example Reviews, Email and SMS marketing, Upsell and bundles, Page builders). The actor recognizes about 80 of the most common apps, including Klaviyo, Yotpo, Judge.me, Gorgias, Attentive, Bold, Privy, ReCharge, Smile.io, and many others. Apps that don't leave a trace in the home page HTML are not detected. -
All app blocks (
shopifyAppBlocks): every app block or extension handle that appears in the page HTML, whether the actor names it or not. This catches apps outside the 80-name list.
All three fields are null on non-Shopify pages. For Shopify stores, shopifyTheme is null if the page doesn't expose it (headless stores), and shopifyApps / shopifyAppBlocks are empty lists (not null) if no apps are visible in the home page.
Limits: Only apps that appear in the home page HTML are detected. Apps that run only on other pages (checkout, account, product pages) or that leave no HTML trace are missed. Theme detection depends on the page declaring Shopify.theme in its JavaScript; headless Shopify stores that don't expose this will show null.
Pricing
Pay per event:
- US$0.002 per website analysed (status
ok, eventdomain-analyzed). Blocked, robots-disallowed, failed (error) andskippedURLs are not charged. - Our start charge, once per run: US$0.00005 per GB of run memory, minimum one GB (event
apify-actor-start). At this actor's default 256 MB that is US$0.00005 per run; at its 2 GB maximum, US$0.0001.
There's no extra charge for platform usage. For example, a run of 1,000 websites of which 950 are analysed costs 950 × US$0.002 + US$0.00005 = about US$1.90. Apify shows the price before you run, and you can cap the cost of a run: if you set a maximum charge per run, the actor stops starting new URLs when it is reached, returns the remaining URLs as free skipped rows, and says so in the run's OUTPUT record.
Privacy
The actor fetches public pages you name. It stores no cookie values, no full page content and no personal data; the output describes software used by websites. When includeEvidence is on (the default), the evidence field keeps short matched snippets of response headers and HTML (for example a Server header value, a script URL or a meta tag) so you can check each finding; switch includeEvidence off to omit them. The user agent identifies the actor: TechStackDetector/0.1 (website technology lookup; one request per page; honours robots.txt).
Support
Questions and bug reports: open an issue in the Issues tab on this actor's page. We aim to respond within 14 days. Replies are written with AI assistance; a human owner can be reached on request.
About
Published by Madrasco. AI agents build and maintain the actor and write its documentation. The signatures are our own work; no third-party fingerprint database is bundled. Product names are trademarks of their owners and are used only to identify the technologies detected.