# Server Header: Fingerprinting and How to Hide It

> The Server header names the software that answered. Use it to find which layer replied, and hide version numbers in nginx, Apache and Cloudflare, with limits.

Source: https://howhttpworks.com/headers/server
Last reviewed: 2026-10-04

> **TL;DR:** `Server` tells the client which software answered, such as `nginx/1.27.2`. It is most useful to you for working out which layer in front of your app produced a response. Dropping the version number (`server_tokens off`, `ServerTokens Prod`) is cheap, but it is minor hardening, not a security control.

## Format

```http
HTTP/1.1 200 OK
Server: nginx/1.27.2
```

The value is one or more product tokens with optional comments, most specific first. RFC 9110 section 10.2.4 says a server SHOULD NOT generate a Server field containing needlessly detailed information, and notes that generous values can be used for fingerprinting. It is optional: origin servers MAY send it.

## Using it for diagnosis

When a request fails, the `Server` header is one of the quickest ways to say which hop spoke last. Examples of what you will meet:

| Value | Layer |
| --- | --- |
| `cloudflare` | Cloudflare edge. Pair with the `CF-Ray` header. |
| `AmazonS3` | S3 directly or behind CloudFront. |
| `awselb/2.0` | An AWS Application Load Balancer answering itself, typical on a [502](https://howhttpworks.com/status-codes/502) or 503 it generated. |
| `envoy` | Envoy, as used by many service meshes and gateways. |
| `nginx` | nginx as a web server, ingress controller, or reverse proxy. |

An error page with `Server: nginx` could be the ingress in front of your app, or the app container's own nginx. Look at the response body, then at `Via` and any vendor header. Behind a CDN, compare `curl` against the public hostname with `curl` against the origin IP using `--resolve`.

## Hiding the version

### nginx

```nginx
http {
    server_tokens off;
}
```

The nginx documentation describes `server_tokens` as enabling or disabling "emitting the nginx version on error pages and in the Server response header field". After `off`, the header is `Server: nginx` and error pages still show a bare `nginx` footer. The directive does not delete the header.

Options for going further:

- The third-party **headers-more** module: `more_clear_headers Server;` or `more_set_headers 'Server: web';`.
- In the commercial nginx Plus, `server_tokens` accepts a string, and an empty string disables the `Server` field entirely (available since 1.9.13).
- `server_tokens build;` does the opposite for debugging: it adds the build name to the version (since 1.11.10).

### Apache

```apache
ServerTokens Prod
ServerSignature Off
```

`Prod` yields `Server: Apache`. The other `ServerTokens` levels are `Major` (`Apache/2`), `Minor` (`Apache/2.4`), `Min` (`Apache/2.4.x`), `OS`, and `Full`, which adds the operating system and compiled-in modules. `ServerSignature Off` removes the version footer from error pages. `Prod` is the floor for the core directive: it does not delete the header.

### Cloudflare and other CDNs

Proxied responses carry `Server: cloudflare`. Cloudflare's Response Header Transform Rules cannot modify the `server` header, or any `cf-` or `x-cf-` header. Your origin's `Server` value is replaced in any case, so work at the origin only to stop it leaking when the origin is reached directly.

### Node, Go and others

Frameworks usually do not set `Server`; they set [X-Powered-By](https://howhttpworks.com/headers/x-powered-by) instead. A Go `net/http` server sends no `Server` header unless you add one. Check what your reverse proxy adds with `curl -sI`.

## How much does it help

Hiding the version stops a header-only banner grab and silences scanners and audit checklists that flag it. Beyond that:

- Exploit scanners do not trust headers. They send the exploit or probe a version-specific file and see what happens.
- Software shows itself through default error pages, the order and spelling of other headers, supported TLS and HTTP/2 behaviour, and static paths.
- A CVE for the exact version you run is the real risk, and removing a string does not change it.

Keep the product name (`nginx`) if it helps your own debugging, drop the version, and spend the effort on patching and on limiting what is reachable. The Express documentation takes the same position about `X-Powered-By`: it may discourage a casual exploit, but it does not stop a determined attacker identifying the framework.

## Verify

```bash
curl -sI https://example.com | grep -i '^server'
curl -s -o /dev/null -D - https://example.com/does-not-exist | head -5
```

The second command checks the error response too, since version strings often survive on 404 and 500 pages and on responses from a different virtual host.

## Related

- [X-Powered-By](https://howhttpworks.com/headers/x-powered-by), [Via](https://howhttpworks.com/headers/via), [User-Agent](https://howhttpworks.com/headers/user-agent)
- [502 Bad Gateway](https://howhttpworks.com/status-codes/502)
