We shipped a bug. A well-known technology publication came back from our auto-detector labelled Shopify. It is not Shopify. It runs Ghost, and it has for years.
The cause turned out to be a single line of detection logic that almost every CMS detector has some version of. It is worth explaining properly, because it tells you something useful about how much to trust any of these tools โ including ours.
The failing case
404 Media is a journalist-owned tech publication running on Ghost. Like a lot of independent publishers, it sells merchandise, and the merch store runs on Shopify.
So the homepage contains a link like this:
<a href="https://404media.myshopify.com/">Shop</a>Our Shopify signature, inherited from the codebase we built on, tested for the string .myshopify.com anywhere in the HTML. That link matched. The detector reported Shopify with full confidence and stopped looking.
The site is Ghost. It says so in its own generator tag, on the same page. We had the correct answer available and reported the wrong one.
The two mistakes
There were two separable errors, and the second is the more interesting one.
Mistake 1: matching a string a site could merely link to
.myshopify.com appearing in HTML does not mean the page is Shopify. It means the word appears on the page. Those are very different claims.
A Shopify storefront genuinely running Shopify exposes things a link cannot fake:
- a
Shopify.themeobject in inline JavaScript, with an ID and role - a
Shopify.shopruntime variable - assets served from
cdn.shopify.comor/cdn/shop/
Those are runtime artifacts of Shopify actually rendering the page. A .myshopify.com link is just text that any site can contain โ a blog post about Shopify, a comparison article, a link to your own store.
We removed the bare string test. Shopify now matches only on the runtime objects and the asset hosts. Both allbirds.com and gymshark.com still detect correctly, so the stricter test lost nothing real.
Mistake 2: letting a heuristic outrank a declaration
This is the deeper fix.
Our detector ran a list of signature tests first โ asset paths, script markers, inline objects โ and only fell back to the <meta name="generator"> tag if none matched.
That ordering is backwards.
Every signature test is a heuristic. It infers a platform from a side effect. Any of them can fire on a site that merely embeds or links to another platform. The generator tag is a declaration: the CMS naming itself, in a field that exists for exactly that purpose.
A guess should never outrank a statement of fact. We moved the generator check to the front. If a site declares itself as Ghost, we do not then decide it is Shopify because a string matched somewhere in the body.
Before: signature list โ (if nothing matched) generator tag
After: generator tag โ (if absent) signature list404 Media now detects correctly, and so does every other publication that links to a store, embeds a Shopify buy button, or writes about e-commerce platforms.
Why nearly every detector has this problem
Detection tools are built from accumulated pattern lists. Someone notices Shopify sites contain myshopify.com, adds the test, and it works on every site they try โ because they test it on Shopify stores.
The failure only appears on sites that reference a platform without running it. Those sites are a minority, so the bug survives for years. It is a false positive that is invisible until you look for it specifically.
You can see the same failure mode in other detectors:
HubSpot false positives. Plenty of tools test for js.hs-scripts.com or a bare hs-sites.com link. Both appear on any WordPress site that embeds a single HubSpot form. We deliberately restrict our HubSpot test to hs_cos_wrapper, hubfs/raw_assets, and hub_generated โ markers that only appear when HubSpot is rendering the page, not when it is embedded in someone else's.
WooCommerce reported as the platform. WooCommerce is a WordPress plugin. Tools that treat it as a top-level CMS report a WordPress site as something else entirely.
Generic path collisions. /templates/ and /themes/ are used by Joomla, PrestaShop, OpenCart, and a long tail of custom builds. Any detector matching those alone will produce confident nonsense on a homegrown site. We check the more specific platforms first for exactly this reason.
An honest false positive we cannot fix
Here is one worth knowing about, because it looks like a bug and is not.
Run moodle.org through a CMS detector and many will tell you it is WordPress. That is correct. Moodle's own marketing homepage genuinely runs WordPress. The Moodle application โ the learning platform itself โ runs at a different URL.
A detector reporting WordPress for moodle.org is right about the page it was given. It is only wrong if you expected it to reason about what the organisation is famous for, which is not something a detector can do. Point it at an actual Moodle instance and it detects Moodle.
This is a useful reminder: a detector tells you what a specific URL runs, not what a company builds.
How to spot a false positive yourself
Three checks, in order of speed.
1. Read the generator tag. View source, search for name="generator". Most CMSs declare themselves. If it says Ghost and a tool says Shopify, trust the generator tag.
2. Ask whether the evidence is runtime or referential. Search the source for whatever the tool claims to have found. Is it a script that executes โ Shopify.theme, M.cfg, var prestashop โ or is it a URL in an anchor tag? Runtime objects are strong evidence. Links are not evidence at all.
3. Check what is serving the assets. Look at where CSS and JavaScript actually load from. A real Shopify store serves from cdn.shopify.com. A Ghost publication serves from /assets/built/. Asset origin is difficult to fake accidentally, because it reflects what actually rendered the page.
What we changed, and what we now avoid
The rule we adopted after this bug, and wrote into our own contributor docs:
Never add a signature that matches a string a site could merely be linking to.
It sounds obvious written down. It was not obvious in the code, where the test was one line among dozens and passed every case anyone had tried.
We also stopped treating detection confidence as binary. Some platforms name themselves clearly. Some can be inferred from runtime artifacts. Some โ Ghost themes, for instance โ genuinely cannot be identified from public HTML at all. A tool that reports the same confidence in all three cases is hiding information you need.
You can try the corrected detector on our homepage, or go straight to the Shopify or Ghost checkers. If you find a site we get wrong, we would genuinely like to know โ that is how this one got fixed.