URL: /rules/security/version-disclosure

---
title: "Version-revealing headers: why Server and X-Powered-By should not name a version"
description: "A Server or X-Powered-By header with a version number tells a scanner which known vulnerabilities to try. squirrel reads five headers and flags each version it finds."
---

## What version disclosure is

Some responses name the software behind them. `Server: Apache/2.4.41 (Ubuntu)`, `X-Powered-By: PHP/7.4.3` and `X-AspNet-Version: 4.0.30319` each put an exact version in front of every visitor. A scanner reads that and tries the vulnerabilities that version is known to have.

The header does not make the site insecure by itself, and a product name without a version is harmless (`Server: nginx`). The version is the part to remove, because it turns a generic probe into a targeted one. The fix is usually one line in the server or framework config.

## What squirrel checks

The rule reads five headers on every crawled page: `Server`, `X-Powered-By`, `X-AspNet-Version`, `X-AspNetMvc-Version` and `X-Generator`. It emits one check named `version-disclosure` per distinct header value across the site.

- **Warn.** A header value contains a dotted version number, at least major.minor. Message: `{header} header reveals a software version: {value}`, naming the header and the value (cut at 120 characters; the full value is kept for machine readers). The check's item lists every page the value was seen on.
- **Not reported.** A product name with no version, such as `Server: nginx`, `Server: cloudflare` or `X-Powered-By: Express`.
- **Not reported.** A single number with no dot, such as `X-Generator: Drupal 9`. The pattern is strict on purpose, so a short value does not raise a false alarm.
- **Pass.** No header on any crawled page names a version.
- **Skipped.** The crawl produced no pages.
- **Skipped, older crawl.** Page records stored before squirrel recorded `X-Powered-By`, `X-AspNet-Version`, `X-AspNetMvc-Version` and `X-Generator` cannot show those headers were absent, so the rule does not pass on them. Check name `version-disclosure-unrecorded`, message `X-Powered-By, X-AspNet-Version, X-AspNetMvc-Version, X-Generator were not checked: this crawl's page records predate them. Re-run the audit with --refresh to check them`. A version in `Server`, which was always recorded, is still reported.

Two pages that report the same `Server` value produce one finding, not two. Two different values for the same header produce one finding each.

## How to fix it

Remove the version from the header, or the header entirely:

```text
# nginx
server_tokens off;

# Apache
ServerTokens Prod
ServerSignature Off

# PHP (php.ini)
expose_php = Off
```

For ASP.NET, turn off the version headers in `web.config` (`<httpRuntime enableVersionHeader="false" />`) and set `MvcHandler.DisableMvcResponseHeader = true` in `Global.asax`. Remove `X-Powered-By` and `X-Generator` at the framework or at the reverse proxy; most frameworks have a single setting for each.

Check the result with `curl -sI https://example.com | grep -i -E 'server|powered|aspnet|generator'` after the change.

| | |
|---|---|
| **Rule ID** | `security/version-disclosure` |
| **Category** | [Security](/rules/security) |
| **Scope** | Site-wide |
| **Severity** | warning |
| **Weight** | 3/10 |

## Enable / disable

### Disable this rule

```toml squirrel.toml
[rules]
disable = ["security/version-disclosure"]
```

### Disable all Security rules

```toml squirrel.toml
[rules]
disable = ["security/*"]
```

### Enable only this rule

```toml squirrel.toml
[rules]
enable = ["security/version-disclosure"]
```

`enable` replaces the default `["*"]`, so this is the only rule selected. Leave `disable` out: a rule that matches any `disable` pattern is skipped even when `enable` names it. Only an `enabled` setting under a rule's own `[rule_options]` table overrides both lists.

## Related rules

- [security/security-txt](/rules/security/security-txt): the other passive check on what a site publishes for scanners and researchers.
- [security/x-content-type](/rules/security/x-content-type): another single-header fix to make in the same server config.
- [security/referrer-policy](/rules/security/referrer-policy): the remaining response header in this group.

Security findings ship in every audit next to the SEO, performance and agent experience rules. See [Website security scan with AI](https://squirrelscan.com/learn/audit-website-security-with-ai) for how an agent works through a report.

## References

- [Server header, MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server)
- [OWASP: Fingerprint Web Server](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/01-Information_Gathering/02-Fingerprint_Web_Server)

## Check your site

Run `squirrel audit https://example.com` and open the Security section of the report. Each distinct value is listed once, with the pages it appeared on. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

<CardGroup cols={2}>
  <Card title="Install squirrel" icon="terminal" href="/quickstart">
    One command to install, one to run your first audit.
  </Card>
  <Card title="See a sample report" icon="file-text" href="https://squirrelscan.com/reports/squirrelscan-sample">
    What the Security section looks like on a real site.
  </Card>
</CardGroup>
