What does `server_tokens off;` in nginx actually remove from responses, what does it leave in place, and how much security does it buy?
answer
- hides the version, not the product
- Server: nginx still goes out
- open source has no directive to remove it
- obscurity, not a control
- pair it with hiding upstream banners
basics
~20 sserver_tokens off removes the nginx version number from the Server response header and from nginx's generated error pages. The header itself still reads "nginx", so the software is still identifiable. It is obscurity, not a control that stops any attack.
solid answer
~50 sWith the default `server_tokens on;`, nginx emits `Server: nginx/1.25.3` and prints the same version in the footer of its own error pages. Setting `server_tokens off;` — valid in `http`, `server` or `location` context — strips the version, leaving `Server: nginx` and an unadorned error page. It does not remove the header, and open-source nginx has no directive that does; the `build` and arbitrary-string forms of the directive are commercial subscription features. To drop or rewrite the field entirely you need the third-party headers-more module and `more_clear_headers Server;` or `more_set_headers`. The honest framing in an interview is that this is defence in depth at best: version fingerprinting is possible from behaviour, TLS characteristics, error-page wording and header ordering, and a scanner that cares will find out anyway. Turn it on because it costs nothing and shrinks the trivially-scriptable target surface — not because it patches anything.
code
nginx · 17 lineshttp {
server_tokens off;
server {
listen 443 ssl;
server_name www.example.com;
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNet-Version;
error_page 404 /404.html;
location = /404.html {
root /srv/errors;
internal;
}
}
}go deeper
Know the directive name and that it strips the version number from the Server header and error-page footer while leaving the word nginx. Say plainly that the header itself remains.
Explain the contexts it applies in and its limits: it does not remove the field, it does not touch upstream headers, and open-source nginx needs headers-more to go further.
Place it correctly in a hardening baseline and be willing to say it stops nothing on its own, listing the fingerprinting routes that survive it — error-page bytes, header ordering, TLS characteristics, protocol edge cases.
Argue the priority order out loud: patch cadence, exposure reduction and authorisation come first, and banner suppression is the last and cheapest item. Resist a compliance ask that treats it as a substitute for any of those.
## What the directive controls `server_tokens` decides how much nginx says about itself in two places: 1. The `Server` response header field, present on every response. 2. The footer of error and redirect pages that nginx generates itself. ```nginx http { server_tokens off; } ``` With it `on` (the default) you get `Server: nginx/1.25.3` and a 404 page reading `<hr><center>nginx/1.25.3</center>`. With it `off` you get `Server: nginx` and `<hr><center>nginx</center>`. The directive is valid in `http`, `server` and `location` contexts and, like most nginx directives, inherits downward — set it once in `http` and every virtual host follows unless one overrides it. Open-source nginx supports `on` and `off`. The `build` value and the arbitrary-string form (`server_tokens my-gateway;`) are part of the commercial subscription, which is worth knowing so you do not put a config in a pull request that will not load on the build you actually run. ## What it does not do It does not remove the `Server` header. There is no directive in open-source nginx that does. If your compliance report demands the field be gone, you need `ngx_headers_more`, a third-party module normally added at build time with `xcaddy`-style tooling for Caddy or, for nginx, `--add-module` or a distribution that already bundles it: ```nginx more_clear_headers Server; # remove it more_set_headers "Server: edge"; # or replace it ``` It also does not touch headers your *application* emits. A backend that sends `X-Powered-By: Express` or a framework banner will keep doing so; nginx passes upstream response headers through. Strip those at the proxy with `proxy_hide_header X-Powered-By;` if you want them gone. And it does nothing about the error page *content* beyond the footer. If you want a page that reveals nothing at all, point `error_page` at your own static file. ## Fingerprinting still works The reason experienced interviewers treat this as a low-value control is that identifying nginx does not require its own cooperation: - **Header ordering and casing.** Servers emit fields in a characteristic order. - **Error-page bytes.** The default markup is distinctive even without the version line. - **Protocol edge cases.** How a server answers a malformed request line, an oversized header, or an unusual method is a fingerprint. - **TLS handshake characteristics** and the cipher preference order the build was compiled with. - **Default artefacts.** The `/50x.html` default page shipped by most packages. An attacker who wants your version can also simply try exploits for several versions; the cost of guessing wrong is low for them. ## So why set it Because it is free, it removes you from the results of the mass scanners that grep banner text for vulnerable version strings, and it prevents an accidental disclosure the day someone discovers your fleet has been on an unpatched build for eight months. It belongs in a hardening baseline alongside the things that *do* stop attacks: ```nginx server_tokens off; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options DENY always; add_header Content-Security-Policy "default-src 'self'" always; proxy_hide_header X-Powered-By; ``` The framing that lands well in an interview is a hierarchy: patch on a schedule, limit what is exposed, authenticate and authorise properly — and *then* turn off the banner. A candidate who presents `server_tokens off` as a security measure rather than as tidiness is signalling that they have confused obscurity for control, which is exactly what the question is testing.
- How do you remove the `Server` header entirely rather than just the version?Open-source nginx cannot: the directive only has `on` and `off`, and `off` still emits `Server: nginx`. You need the third-party headers-more module compiled in, then `more_clear_headers Server;` to remove it or `more_set_headers "Server: ..."` to replace it. nginx Plus additionally supports `server_tokens build` and an arbitrary string value.
- Your backend still leaks `X-Powered-By` through nginx. How do you stop that at the proxy?`proxy_hide_header X-Powered-By;` in the relevant location or server block. nginx forwards upstream response headers by default, and `server_tokens` governs only nginx's own banner. Fixing it at the application is better, but the proxy hide is the immediate stopgap and useful when the upstream is third-party software you do not control.
saying these in an interview costs you the question
- Claiming it removes the Server header completely
- Treating banner suppression as a real security control
- Assuming it also hides upstream headers like X-Powered-By
- Thinking version disclosure is the only fingerprinting route