phpinfo() on a public URL: the full picture of your server
Published · updated
A single leftover file can publish the exact PHP build, every loaded extension, absolute paths, database hostnames and the environment variables of the process. It is the most complete reconnaissance page a server can offer.
What the page actually contains
- The exact PHP build —
PHP Version 8.2.29. Unlike a header, it cannot be absent or approximate. - Loaded extensions and their versions — which tells an attacker which known bugs apply.
- ini paths and values — where configuration lives, how large uploads may be, whether errors are displayed.
- Environment variables — often
DB_HOST,DB_USER, sometimesDB_PASSWORD, cloud keys and SMTP credentials. - Absolute paths, server software, the document root — the map for every later step.
With the build and the environment together, an attacker has both the vulnerability to try and, frequently, the credentials to skip it.
Where the leftovers come from
- Hosting “check” pages. Providers ship
info.phporphpinfo.phpto verify the stack and never remove them. - Migrations. A test file created to prove the new server works, then forgotten in the deploy.
- Monitoring and panels. Old monitoring agents,
/server-statusand/server-infofrom Apache'smod_statusandmod_info. - Debug toolbars and framework pages left on in production — they can print the same values without the word phpinfo appearing anywhere.
Find them
# снаружи: обычные имена, что опрашивают боты for p in phpinfo.php info.php test.php php_info.php server-info server-status; do printf '%-16s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' "https://example.com/$p")" done # на сервере: ищем вызов по содержимому, а не по имени grep -rl --include='*.php' 'phpinfo()' /var/www 2>/dev/null find /var/www -name 'phpinfo*.php' -o -name 'info.php' -o -name 'test.php' 2>/dev/null
Check the result against the real page: a 200 can be the site's catch-all page rather than a phpinfo. The body must contain PHP Version and phpinfo() in the title. Our checker reports this as a critical finding, separately from the version header, because a hidden X-Powered-By next to an open info.php is a false sense of safety.
Close it, then assume it was read
- Delete the file. Not rename, not restrict — delete. A test page has no runtime purpose.
- Rotate what the page printed. Any credential that appeared in the environment section: database, SMTP, cloud keys. This is the step people skip and regret.
- Turn off
expose_phpso the build is not advertised in headers either, and setdisplay_errors = Offin production. - Block the patterns so the next leftover fails closed: deny
phpinfo.php,info.php,/server-info,/server-statusat the web server, and disablemod_infoif it is not needed. - Read the log. Requests for
/phpinfo.phpfrom addresses that are not yours, with a 200 in the same line, mean the page was harvested.
After the cleanup
Re-run the check: the finding should disappear and the build row should move to “not disclosed”. Then keep the release pipeline honest — a request to /phpinfo.php that returns 200 should fail the deploy, the same way an exposed .env should. A checksum baseline catches the next file that appears without a deploy.
Questions
Does deleting the file fix the leak if it was public for weeks?
It stops future reads. The leak already happened: rotate the credentials the page exposed and check access logs and admin activity.
Cloudflare hides my server — is a public phpinfo still a problem?
A CDN hides the origin only if direct requests to the origin are blocked. Otherwise the file is reachable by IP, and search engines or bots that find the IP skip the CDN entirely.
What is the difference between /server-info and phpinfo()?
/server-info lists Apache modules and configuration; phpinfo() prints the PHP side, including environment variables. Both are reconnaissance, and both should be closed.
Can I keep phpinfo but password-protect it?
You can, and it is still a liability: authentication on a diagnostic page is one misconfiguration away from public. Delete it and use the panel or the CLI when you need the same facts.