Your site already tells everyone what it is made of.
Enter a URL and we will name the builder, framework, database, auth provider, host and server it publishes about itself, then run the four checks that only make sense once we know. Nothing here is guessed from a probe: it is all in the response your visitors already receive.
What this checks, and what a good answer looks like
What we can see, and why it is visible at all
None of this is hidden. A framework announces itself because it has to: Next.js loads its bundles from /_next/static/, Vite emits a predictable asset path, Supabase is called at a project URL that has to be in your JavaScript for the browser to reach it, and a great many servers still send a Server header with a version number in it. We read the same page and the same scripts a browser does, and match what comes back against a table of markers. There is no path guessing here, and there is no request your own visitors do not already make.
A development server that got deployed
The strongest finding on this page, and a surprisingly common one. Vite's development server is not a web server: it reads from your project directory on every request, compiles on the fly, and exposes a /@fs/ route that can reach outside the project root entirely. It exists so that saving a file updates your browser, and it was never written to face the internet. It reaches production the same way every time: someone runs npm run dev, points a tunnel or a hosting service at it to show a colleague, and that becomes the deployment. If the page loads /@vite/client, that is what happened.
A development build where a production build belongs
React, Vue and Angular each ship two builds. The development one keeps the warnings, the component names and the property-type checks; the production one strips all of it. Serving the development build is not a hole an attacker walks through, but it is several times more JavaScript than the page needs, measurably slower to run, and it describes your component tree to anyone who opens devtools. We only ask this question once we have recognised a framework that has two builds to choose between, because on a plain static page there is no such thing as a development bundle and telling you that you passed would be telling you nothing.
Server data embedded in the page
Next.js writes whatever your server-side props return into the HTML of every page it renders, inside a __NEXT_DATA__ block. That is how the framework works and it is not a flaw. The flaw is the easy mistake next to it: you fetch a user record to display a name, return the whole record, and now the email address, the internal id and the role are in the page source for anyone who presses view-source. We look for field names that read like records rather than display data, and we report the names only, never the values. This one can be a false positive, so we ask for two before we say anything, and we word it as something to look at rather than something to fix.
Software past its support window
When a version is readable, we check it against a small table of end-of-life dates: PHP, Apache, IIS, OpenSSL, jQuery, Bootstrap, AngularJS and Vue 2. Being out of support does not mean the site has been broken into. It means the reason you are entitled to feel safe, that fixes keep arriving, stopped applying on a particular date, and everything found since then is still there. We tell you the date so you can check us. What we deliberately do not do is name a CVE. That would need a vulnerability database and version-range matching we do not have here, and a scanner that guesses at specific bugs is worse than one that tells you the version is old and stops.
Why we do not score any of this
Recognising WordPress is not a finding. Neither is recognising PHP, Vercel or Stripe. Plenty of tools present a technology list as though the entries were charges, and it is the fastest way to make a report untrustworthy: it flags things that cannot be fixed and would not be worth fixing. The stack panel on this page costs you nothing. Only the four checks above can produce a finding, and only when they actually found something.
How to fix it
Three fixes, in the order they are usually needed: stop shipping the dev server, stop shipping the map files and the framework banner, and stop announcing versions you have not upgraded yet. The last one is cosmetic on its own and worth doing anyway, because it is the difference between being targeted specifically and being missed by a scan.
# The dev server is not a web server. It reads from your project
# directory on every request and serves whatever it finds, which is
# why /@fs/ can walk outside the project root at all.
# Wrong, and the single most common way this happens:
npm run dev # then a tunnel or a host pointed at :5173
# Right:
npm run build # emits dist/
npm run preview # serves dist/ locally, to check it first
# Then deploy dist/ as static files. On a host that runs a command
# for you, the build command is 'npm run build' and the publish
# directory is 'dist' (Vite) or 'build' (Create React App).// vite.config.js -- source maps off, which is the default but
// worth stating, because a debugging session that flipped it on
// is how most of them end up in production.
export default {
build: { sourcemap: false },
};
// next.config.js
module.exports = {
productionBrowserSourceMaps: false,
// Also drops the "X-Powered-By: Next.js" banner, which is the
// header that tells a scanner exactly which framework to target.
poweredByHeader: false,
};# nginx: stop announcing the version. server_tokens off leaves
# "nginx" but removes the number, which is the part that tells
# someone which published bugs to try.
server_tokens off;
# Anything proxied from PHP, Express or Rails announces itself too:
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;
# Caddy, equivalent:
# header {
# -Server
# -X-Powered-By
# }
# Express, at the top of the app:
# app.disable("x-powered-by");
# This is cosmetic on its own. It does not patch anything, it just
# stops handing over the version. Upgrade as well.We can only name what the site publishes. A stack that announces nothing is invisible to us, and an empty result here means we recognised nothing, not that there is nothing to recognise. The opposite matters more: a technology we name is a technology we spotted, not one we tested. Reading Server: Apache/2.4.41 tells us the version string, not whether it is patched, and recognising Supabase tells us nothing at all about whether your row-level security is on, which is the question that actually decides whether your data is readable. That one needs the code. If you want the checks that depend on the stack rather than just the stack, the full surface scan runs all of them together and scores the result.