Response headers name the application stack
What this check looks for
Headers such as X-Powered-By tell every visitor which framework and version builds your pages. They serve no purpose for the visitor.
Why it matters
These headers exist only to advertise. They cost bytes on every response and hand an attacker the first thing they would otherwise have to work out.
When the check passes, your report says: “Headers give away nothing about the application stack”.
What it costs your score
When this check fails it removes 3 points from your Web security score, before the status, confidence and repeat multipliers are applied. Web security carries a weight of 8 in the overall score.
It shares the web-security.disclosure family ceiling of 10 points: however many findings that family produces, together they cannot remove more than that from Web security. One underlying problem showing up in several places is still one problem.
- Severity
- low
- Default confidence
- confirmed
- Status when triggered
- warn
- Deduction
- 3 points
- Family cap
- web-security.disclosure · 10
- Category
- Web security
- Module
- Web security headers
- Fix owned by
- user
- In the ruleset since
- 2026.09
How to fix it
Remove the headers that name your stack.
They benefit nobody but someone enumerating targets.
Remove them at the origin, not only at a proxy — a proxy that is bypassed exposes them again.
PHP: set
expose_php = Offin php.ini.ASP.NET: remove the
X-Powered-Bycustom header and setenableVersionHeader="false".Express:
app.disable('x-powered-by').
How to confirm it worked
curl -sSI https://‹host›/ | grep -iE 'x-powered-by|x-aspnet|x-generator|x-runtime' — expect nothing
A named slot like ‹domain› — and the braces left in the configuration below — is filled in with your own values when this rule appears on a report.
Remediation by platform
# nginx cannot unset a header from an upstream without the headers-more module:
more_clear_headers 'X-Powered-By' 'X-AspNet-Version' 'X-Generator';<IfModule mod_headers.c>
Header always unset X-Powered-By
Header always unset X-AspNet-Version
</IfModule>header {
-X-Powered-By
-X-Generator
}app.disable('x-powered-by');Without headers-more, remove the header in the application instead — proxy_hide_header only works for headers from a proxied upstream.
Shown because the response identified Apache. Prefer the virtual host over .htaccess; and if PHP adds the header,
expose_php = Offremoves it at the source.
Technical detail
Present: ‹value›.
This rule owns the **response header** case, including a header that carries a product version such as X-Powered-By: WordPress 6.4. The same version published inside the page — in a generator meta tag or in the markup — is reported once, separately, by web.technology.cms-version-disclosed. Neither rule reports the other's channel, so one header is deducted once.
X-Powered-By, X-AspNet-Version, X-Generator and X-Runtime are all removable at the application or server level, and nothing depends on them. Some are added by the runtime rather than the web server — PHP's expose_php and ASP.NET's httpRuntime settings, for instance — so removing them may mean a change in the application's own configuration rather than the server's.
Standards and references
Test this on your domain
Run the check that produces this finding, on its own, against any domain.
Other web security headers checks
- No Cross-Origin-Embedder-Policy is set
- No Cross-Origin-Opener-Policy is set
- No Cross-Origin-Resource-Policy is set
- No Content Security Policy is enforced
- The policy leaves plugins or the base URL unrestricted
- The Content Security Policy is report-only
- The policy allows eval()
- The policy allows inline scripts or styles
- The policy allows resources from anywhere
- A security header was sent twice with different values
- HSTS max-age is shorter than six months
- HTTP Strict Transport Security is not in force