Skip to content

Test your website now

Free, no card. We read only public pages.

Run free audit →

Security · Low severity

X-Powered-By Header: Remove Unnecessary Stack Disclosure

Remove the X-Powered-By header in Express, PHP, or your proxy. Check related disclosure headers, verify all response paths, and understand the limited benefit.

Published · Updated

What the check tests

Check
sec.disclosure.powered-by - X-Powered-By header removed
Passes when
Responses should not include X-Powered-By (or X-AspNet-Version and friends).
Scope
graded on every page crawled
Severity
Low
Fix effort
small - under an hour
Impact when fixed
low

In one paragraph

The X-Powered-By header advertises software used to produce a response, sometimes including its version. Removing it reduces easy fingerprinting, but does not repair vulnerabilities or make the stack unidentifiable. Disable it at the source or strip it at an appropriate proxy, check related disclosure headers, and verify normal, API, and error responses after the change.

Why it matters

Technology disclosure helps automated tools classify a site before choosing further probes. A header such as X-Powered-By: Express identifies a framework; a value containing a runtime version can supply more detail. Removing unnecessary information is sensible low-effort hardening.

Keep the benefit in proportion. Attackers can often infer software from cookies, asset paths, error behaviour, and many other signals. A version string can also be incomplete or misleading because vendors may backport patches without changing the upstream-looking version. The header alone does not prove the server is vulnerable or unpatched.

Updating dependencies, restricting access, and preventing injection remain more important than concealing a product label. Do not replace truthful vulnerability management with a claim that a stack is secure because its banner disappeared.

How WebAuditOS evaluates this finding

The supplied check reads X-Powered-By and X-AspNet-Version on fetched responses. If either is present, it reports a failure; absence of both passes. Requests with no response are not graded. The described severity is low even though the result label is fail.

This applies beyond HTML: API and asset responses can be served by another component with different defaults. The evidence quotes a value to help identify its owner. The rule does not establish whether a named version has an exploitable issue, nor does it inspect every other possible fingerprint or disclosure header.

How to fix it

  1. Inspect the exact public response named in the finding. Compare a normal page, an API route, a static asset, a redirect, and a controlled error response. Note which layer appears to add each disclosure header.
  2. Disable the header at the component that generates it where practical. In Express, the documented setting is:
app.disable('x-powered-by');

Apply it before serving requests. If a security-header middleware already removes the header, check ordering, custom response handlers, and additional services behind the same domain rather than adding unrelated settings.

  1. For PHP, review the deployed php.ini setting:
expose_php = Off

Ensure the change reaches the actual PHP runtime or pool serving production, and reload through the appropriate deployment procedure. Editing a command-line PHP configuration may not change the web-serving runtime.

  1. For classic ASP.NET deployments, distinguish X-Powered-By from X-AspNet-Version. They can originate from different settings or layers. Review custom response headers in IIS configuration and the framework's version-header option for the application version in use. Do not assume an ASP.NET Core application uses the same configuration model.
  2. If source changes are unavailable or several services need consistent output, remove the header at a suitable proxy or edge layer. For an nginx upstream proxy, examples include:
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;

These directives concern proxied upstream responses; FastCGI and other response sources have their own applicable configuration. For Apache with the headers module, a common starting point is:

Header always unset X-Powered-By

Check the actual result across status codes and modules rather than assuming one directive covers every response source.

  1. Remove duplicate or newly introduced values from other layers. An edge filter can enforce a public policy, but direct-origin endpoints may still expose the header if reachable. Decide which interfaces are intended to be public and audit them accordingly.
  2. Verify with fresh requests, accounting for CDN caching. For example:
curl -I https://example.com/

Check both named headers on representative routes and repeat with GET if HEAD differs. Rescan after deployment. Keep a simple regression assertion in integration tests so a newly added service does not silently reintroduce the header.

What the fix can break

Standard browser rendering does not require this disclosure header, so removal usually has little user-facing risk. Internal inventory scripts, monitoring assertions, or technology-detection dashboards may nevertheless rely on it. Check owned tooling before deployment.

If an intermediary uses the response value for custom processing, removing it earlier in the chain can change that behaviour. Prefer explicit internal metadata or configuration over an accidental dependency on a public technology banner.

Do not spend disproportionate effort rewriting the entire server fingerprint. Once the unnecessary header is gone, prioritise actual software updates and stronger controls. A security audit should record the limited nature of this hardening change rather than presenting it as an exploit fix.

Questions

Does removing X-Powered-By make the framework impossible to detect?

No. Other response and application characteristics can reveal it. Removal reduces one easy signal, not all fingerprinting.

Why does the check still fail after changing Express?

Another route or component may add a header, or X-AspNet-Version may still be present. Inspect the exact URL and quoted value before changing more configuration.

Should I replace the value with a fake framework name?

Usually not. Omit unnecessary disclosure rather than adding misleading data and a new configuration to maintain. False banners do not replace security updates.

Review directory listing, framing protection, and the security headers guide. Exposed files and missing browser protections deserve their own remedies.

In the report

Test this on your own site

Every scan runs sec.disclosure.powered-by on every page it crawls, on every plan. The finding carries this id, so a line in a report can be matched to the explanation above - and the free scan needs no account.