PHP web serversVersion disclosed in headers

Server exposes its PHP version in the X-Powered-By header

PHP adds an X-Powered-By header with its exact version unless expose_php is turned off. That tells anyone, including automated scanners, which vulnerabilities to try. Set expose_php = Off in the server's PHP configuration or hosting panel, remove the header at the web server or CDN if needed, and keep patching: hiding the version reduces targeting but doesn't fix an outdated PHP.

This guide covers check 08: Observability and deployment in the Zenveus Production Readiness Standard.

For builders

What this means, in plain words

Your server may be announcing exactly which PHP version it runs in every response. That makes it easy for automated tools to pick which known bugs to try. Turning the announcement off takes one setting, but it is a small extra layer: the real protection is keeping PHP up to date.

A scoped repair request

Using an AI builder? Paste this

Use this prompt in Lovable, Cursor, Replit, or Claude Code with the relevant server files available.

Check whether my site discloses its PHP or server version. Give me read-only commands to inspect response headers on a normal page and an error page. Then show the smallest configuration change to stop X-Powered-By and version strings on error pages, for my setup (php.ini, Apache or LiteSpeed .htaccess, Nginx, or CDN), and how to verify it. Do not change other headers or security settings.

The same failure may appear as

  • X-Powered-By shows the exact PHP version
  • Server header shows the web server name and version
  • Error pages reveal PHP or framework versions
  • Security scanner flags version disclosure

Find the failure layer

Run these checks before rewriting anything

Each check removes a class of causes. Keep the first failing result, its timestamp, and the production log beside it.

01

Read the headers

Request a normal page and an error page, and look for X-Powered-By, Server and framework-specific headers.

If this failsAn exact version string is the disclosure to remove; a generic server name alone is lower risk.
curl -sI https://example.com/ | grep -iE 'x-powered-by|^server'
curl -sI https://example.com/this-page-does-not-exist | grep -iE 'x-powered-by|^server'
02

Find where PHP settings are controlled

Check whether you can edit php.ini directly or through your hosting panel's PHP options. The PHP manual lists expose_php as a php.ini-only setting, so ini_set(), .htaccess and .user.ini can't change it.

If this failsIf you can't reach a system-level setting, remove the header at the web server, proxy or CDN instead.
03

Check the version itself

Compare the disclosed version with the latest security release for its branch before you hide it.

If this failsAn outdated version needs patching first; hiding the header only removes the hint.

Ranked diagnosis

Common root causes, in the order we would test them

01

Default PHP configuration

Many installations ship with expose_php enabled.

02

No header policy at the edge

The web server, proxy or CDN passes backend headers through unchanged.

03

Verbose error pages

Debug or default error pages print versions even when the header is removed.

Step-by-step repair

How to fix this in your app

Change php.ini where your host allows it (directly or through the hosting panel), or add a header rule at the web server or CDN.

Production safety ruleNever disable access controls, expose service keys, or add wildcard CORS as a routine shortcut.

01

Turn off expose_php

Set expose_php = Off in php.ini, directly or through your hosting panel's PHP options, then restart PHP so every worker picks it up.

02

Strip the header at the edge if needed

If you can't change PHP settings, remove X-Powered-By at the web server, reverse proxy or CDN response rules.

03

Use generic error pages

Turn off display_errors in production and serve custom error pages that don't print versions or stack traces.

Implementation example

Turn off version disclosure

; php.ini (directly or via your hosting panel)
expose_php = Off

# Apache or LiteSpeed, if php.ini is out of reach
Header always unset X-Powered-By

# Nginx in front of PHP-FPM
fastcgi_hide_header X-Powered-By;

Restart PHP after changing php.ini. The Apache directive needs mod_headers; on managed hosting, check which method your host supports.

Prove the repair

How to check that the fix worked

Run these checks with synthetic data in your test environment, then repeat the relevant acceptance checks after deployment.

  • A normal page response has no X-Powered-By header.
  • A 404 or error response has no X-Powered-By header.
  • Error pages show no PHP, server or framework versions.
  • The PHP version itself matches the latest security release for its branch.

If the check still fails

If the header persists, a CDN or cache may be replaying stored responses, or another layer (an application framework or plugin) may be setting it. Purge the cache and check each layer's response headers separately.

When the built-in AI fix makes it worse

Recover one reproducible failure.

Pause generated changes, restore a known working branch, and capture one failing request with its logs. Change one layer and rerun the allowed and denied checks before proceeding.

Engineering handoff

What Zenveus checks when the quick fix is not enough

We trace one production request through the complete path, isolate the failing boundary, and leave behind evidence your team can repeat.

Header review

Version and technology disclosure across pages, errors and APIs.

Patch status

Whether the disclosed versions are current and supported.

Error handling

Production error pages and logging that don't leak internals.

Edge policy

Header rules at the web server, proxy and CDN.

Typical repair pattern

Responses no longer disclose the PHP version

Normal and error responses carry no X-Powered-By header, error pages show no versions, and the PHP build itself is current.

Evidence left behind
  • Root-cause note
  • Verified production check
  • Rollback and prevention steps

Before the next release

Prevent this failure from returning

Include a header check in your post-deploy checks.
Patch on the vendor's schedule regardless of what the headers show.

Clear answers

Questions teams ask before they touch production

01Does hiding the version make the server secure?

No. It removes an easy hint for attackers, but scanners can still try known exploits. Keeping PHP patched is the actual protection.

02Can I set expose_php in .htaccess or .user.ini?

No. The PHP manual lists expose_php as a php.ini-only setting. Change it in php.ini or your hosting panel's PHP options, or remove the header at the web server or CDN instead.

03Should I remove the Server header too?

Reducing it to a generic value is reasonable. Some hosts and CDNs set it themselves, so you may only be able to change it at the edge.

04How do I know the fix worked in my app?

Run the verification checks on this page from outside the server. The X-Powered-By header should be gone from normal and error responses.

Official documentation and library references

Free next step

Check the boundary before you hand it over

The free tool helps you inspect this symptom. Its result does not establish whether the whole app is production ready.

Take it further

Fixed this one? Find the next one first.

This fix solves one symptom. A guided self-assessment walks you through the rest of the app, one session at a time, and ends with a report and a ranked fix list you can act on or hand to a developer.

Planning more than one stage? Founder Bundles combine the guided products from $149 and save up to $405.

The Verdict

Know whether the symptom is contained or structural.

We can see the symptom from here. What we cannot tell you from outside is whether it is contained or structural. A scanner collects evidence. A named senior engineer makes the decision. For $299, a named senior engineer reads your code and signs a written Verdict against the nine checks in the Zenveus Production Readiness Standard. The 48-hour clock begins when the required access and context are available. If the report does not give your developer a list they can act on, you do not pay.

The 48-hour clock starts when the required access and context are available.

Scroll to Top

Optional tracking stays off until you enable it. You can change your choice at any time.

Essential — always active
Security, requested features, and remembering these choices.

Read our cookie policy