How to hide or remove the Server header in nginx and Apache

By default both servers tell every visitor their exact version. One line hides it; removing the header completely needs more than that.

1. Check what you send now

curl -sI https://your-site.com/ | grep -i '^server'

Our tests with the default Docker images gave Server: nginx/1.31.6 and Server: Apache/2.4.68 (Unix): the exact version, which security scanners flag as information disclosure.

2. nginx: server_tokens off

http {
    server_tokens off;
    ...
}

It also works inside a server block. Reload (nginx -t && nginx -s reload) and the header becomes Server: nginx: the version is gone, the name stays. Open-source nginx has no setting to drop the name. People use the third-party headers-more module (more_clear_headers Server;) for that; we did not test it here.

3. Apache: ServerTokens Prod

ServerTokens Prod
ServerSignature Off

Put these in the main server config (httpd.conf or apache2.conf), not in .htaccess, and restart. The header becomes Server: Apache. ServerSignature Off is a precaution for Apache's own error pages; it was already off in the official image.

Header unset Server does not work. We added both Header unset Server and Header always unset Server (mod_headers loaded): the response still said Server: Apache. Apache sets this header itself, and mod_headers can't remove it.

4. X-Powered-By from your app, behind nginx

PHP, Express and other app servers often add X-Powered-By (for example PHP/8.3.0). nginx passes it through unchanged. Hide it at the proxy:

server {
    listen 80;
    server_tokens off;
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_hide_header X-Powered-By;
    }
}

In our test the backend sent X-Powered-By: PHP/8.3.0. Without proxy_hide_header visitors received it; with it, the header was gone. The backend's own Server header was replaced by the proxy's (Server: nginx). You can also switch it off at the source: expose_php = Off in php.ini, or app.disable('x-powered-by') in Express.

5. Results

ConfigServer header sent
nginx defaultnginx/1.31.6
nginx + server_tokens offnginx
Apache defaultApache/2.4.68 (Unix)
Apache + ServerTokens ProdApache
Apache + Header unset ServerApache (unchanged)

Behind a CDN or load balancer, visitors see the proxy's Server header instead. Run the curl check against the public URL, not only the origin. Hiding the version doesn't patch anything, so keep the server updated. To check the rest of your headers (CSP, HSTS, X-Content-Type-Options), use the free security headers generator.

Frequently asked questions

Is showing the server version a vulnerability?

Not by itself, but it tells attackers which known bugs to try, and most scanners report it. Hiding it takes one line.

Can I remove the Server header completely in nginx?

Not with open-source nginx alone: server_tokens off still sends 'nginx'. The third-party headers-more module can clear it (not tested by us).

Why doesn't Header unset Server work in Apache?

Apache adds the Server header itself. In our test both 'Header unset Server' and 'Header always unset Server' left 'Server: Apache'. Use ServerTokens Prod.

Written with AI assistance. All configs were run in the official nginx:alpine (1.31.6) and httpd:alpine (2.4.68) Docker images on 2026-10-01 and checked with curl -I; the X-Powered-By test used an nginx backend that sent the header. The php.ini and Express settings are from their documentation, not tested here. PlainHeaders is our extension; the other methods are free.