X-Powered-By technology disclosure

An X-Powered-By or similar header announces the application platform and often its version (PHP/8.1.2, ASP.NET, Express), narrowing an attacker's search for applicable exploits.

Do this: Remove X-Powered-By in the app or the proxy. It names your framework and version, which narrows an attack from guesswork to a known CVE list.
PassNo X-Powered-By or platform version header is present.
LowA platform disclosure header (X-Powered-By or similar) is present.

The fix, in one snippet

Example to adapt PHP / Express
expose_php = Off              ; php.ini
app.disable('x-powered-by')   // Express

Illustrative values. Change the paths, hostnames and options to match your own site before using it.

The sections below explain what is tested, why it matters and the alternatives.

What we test

Scan.now reads the X-Powered-By, X-AspNet-Version and X-AspNetMvc-Version response headers on /. Any of them being present fails the check at low severity; a value containing a version number (PHP/8.1.2, ASP.NET 4.0.30319) is highlighted over a bare product name (Express, Next.js). The report shows the exact header and value. Headers such as X-Generator and <meta name="generator"> tags are handled by generator-disclosure, and the web server's own banner by server-header-disclosure. Scan.now does not fingerprint the platform by other means; it only reports what the response volunteers.

Why it matters

Knowing the runtime tells an attacker which class of bugs to look for: deserialisation issues in one framework, request-smuggling quirks in another, argument-injection in a specific PHP-CGI setup (CVE-2024-4577 affected PHP on Windows and was mass-exploited within days of disclosure). A version pinpoints whether a public exploit applies. The header adds nothing for legitimate clients; it exists because PHP's expose_php and Express's default settings ship enabled. As with the Server banner, removing it is hygiene rather than a fix, and your real defence is keeping the runtime patched. But there is no reason to make the attacker's shortlist for them, and several compliance checklists (PCI DSS scan vendors, for example) flag the header. See the security headers guide for how this fits with the other headers.

How to fix it

PHP, in php.ini (then restart PHP-FPM or Apache):

expose_php = Off

Express:

app.disable('x-powered-by');
// or, if you use Helmet, helmet() removes it for you

ASP.NET, in web.config:

<system.web>
  <httpRuntime enableVersionHeader="false" />
</system.web>
<system.webServer>
  <httpProtocol>
    <customHeaders>
      <remove name="X-Powered-By" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

If you cannot change the application, strip the header at the proxy. nginx in front of an upstream:

proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;

Apache: Header unset X-Powered-By. Next.js sets poweredByHeader: false in next.config.js. Cloudflare passes origin headers through unchanged, so a Transform Rule with "Remove response header" is needed if the origin cannot be fixed.

Where this fits

X-Powered-By technology disclosure is check 10 of 13 that the website vulnerability scanner runs under http security headers, ordered the way they are worth fixing. That ordering is the point: Fixing this one while the check above it still fails buys less than it looks like.

Fix this one first

Above it in the same category sits X-Content-Type-Options: nosniff (low), where responses lack X-Content-Type-Options: Nosniff, so browsers may guess a content type and execute a file as script or style when the server said it was something else. An attacker who has that does not need this, so it is the better use of the same hour.

What fixing this still leaves open

X-Powered-By technology disclosure closes one route in. Immediately below it: Cache-Control on pages that set cookies, where a response that sets cookies, and is therefore personalised, does not tell caches to keep out; Cross-Origin-Resource-Policy, where responses do not carry Cross-Origin-Resource-Policy, so other origins may embed them as scripts, images or fetches in no-cors mode and pull them into their process; X-XSS-Protection (deprecated header), where the response sets X-XSS-Protection to 1 or 1; mode=block.

Found in the same scan

The website vulnerability scanner reports this alongside checks from other categories that are at least as serious, including Certificate chain and hostname validation, where the certificate presented for this hostname did not validate: The chain does not reach a trusted root, an intermediate is missing, the name does not match, or the certificate is self-signed or expired, and Exposed .env configuration file, where a .env configuration file is served from the web root. A single run of website vulnerability scanner answers all of them at once.

Prompt for an AI Hand this check to an assistant Sign in to copy it
The first few lines
You are a senior web engineer. I ran a security and SEO scanner against my site and it reported the finding below. Fix it properly rather than suppressing the symptom.

Finding: X-Powered-By technology disclosure (low severity)
Scanner check id: x-powered-by-disclosure
18 more lines, including the evidence and the exact fix

The rest of this prompt names the pages and line numbers we found the problem on, the configuration to change, and the constraints a good answer has to respect. It is free, it just needs an account so the work is not scraped wholesale.

Sign in with Google

Signing in is free and takes one click. We store your email address and nothing else.

References

  1. PHP: expose_php
  2. Express: Production best practices, security
  3. OWASP HTTP Security Response Headers Cheat Sheet

Related guides