The corrupted WordPress db.php dropin nobody warned you of
WordPress says the DB connection is down but MySQL is fine, and only one site is affected. The cause is almost always a broken db.php dropin in wp-content.
The corrupted WordPress db.php dropin nobody warned you about
The ticket reads "Error establishing a database connection". You SSH
into the box. MySQL is up. mysql -u root -p works. The other twelve
WordPress sites on the same server are loading fine. Only this one
site is broken, and only with that one specific message, and nothing
in the server log explains why.
This is one of those incidents that wastes an hour every time we
forget about it. The cause is almost always the same and it has
almost nothing to do with the database: a corrupted, mis-owned, or
stale db.php file in wp-content. WordPress is not failing to
talk to MySQL. WordPress is failing to load a dropin file that it
found, decided to use instead of its core connection logic, and
then could not execute.
This post is the short version of the use case. What a dropin actually is, the sixty-second check, the one-line fix, and the four ways it gets corrupted in the first place.
The symptom in detail
The standard "Error establishing a database connection" page comes
from wp-includes/load.php when core's wpdb class cannot
connect. The reflex is to blame MySQL. On a multi-tenant cPanel
server that reflex is wrong about half the time; the other half it
is a dropin.
The tell is the same short list every time:
mysql -u root -pfrom the shell workssystemctl status mariadb(ormysqld) shows the service active- Other WordPress sites on the same server load normally
- The credentials in
wp-config.phpare unchanged from yesterday - The site's own database is reachable using those exact credentials
if you run
mysql -u <wp_user> -p <wp_db>from the cPanel user shell and try aSELECT 1
If all five are true and the WordPress page still says it cannot
connect, check wp-content/. Not wp-config.php. Not the
database. The dropins directory.
What a WordPress dropin actually is
A "dropin" is a specific small set of file names that WordPress
core looks for in wp-content/ on every page boot. If one exists,
core loads it and uses it in place of the default behaviour for
that subsystem. It is not a plugin, it is not registered through
the admin, it does not appear in the Plugins screen. It is loaded
by core if and only if the file exists at the right path.
The four that show up in agency-shaped incidents are:
wp-content/db.php: replaces the defaultwpdbdatabase connection classwp-content/object-cache.php: replaces the default object cachewp-content/advanced-cache.php: page cache loaded before most of WordPresswp-content/maintenance.php: replaces the default "briefly unavailable" page
db.php is the one this post is about because it is the one whose
failure mode masquerades as a MySQL outage and gets the on-call
engineer chasing the wrong problem.
Almost every caching or database-optimisation plugin installs a
db.php. W3 Total Cache installs one for query caching. LiteSpeed
Cache installs one. Redis Object Cache installs one (or installs
object-cache.php, depending on version). Activating the plugin
copies the file into wp-content/db.php. Deactivating, if the
plugin is well-behaved, removes it. When the plugin is updated
incompletely, removed sloppily, or the file gets corrupted by some
other process, you get the symptom in this post.
The sixty-second check
Three commands. From the cPanel user shell, in the document root of the affected WordPress install:
# is there a db.php at all?
ls -la wp-content/ | grep -E 'db\.php|object-cache\.php|advanced-cache\.php'
# who owns it, and can the PHP process read it?
stat wp-content/db.php
# whose plugin claims it?
head -20 wp-content/db.phpThe ls -la answers "does the file exist". On a healthy install
with no caching dropin, the answer is no. On a site running W3
Total Cache or LiteSpeed Cache or Redis Object Cache, the answer is
yes and that is expected.
The stat answers the next-most-common cause: ownership. PHP-FPM
under cPanel runs as the cPanel user (for example, goldenvi). The
dropin needs to be readable by that user. If someone has done a
chown nobody:nobody somewhere up the tree, or if a backup restore
handed the file back with root:root permissions, the PHP process
cannot read it and WordPress falls into a fatal error path that,
depending on the WordPress version, presents as the generic
database-connection error.
The head answers which plugin owns the dropin. Every well-behaved
caching plugin ships a db.php with a header comment that names
the plugin:
<?php
/**
* Plugin Name: W3 Total Cache Database Cache Drop-In
* Version: 2.x.x
* Author: BoldGrid
*
* This is a drop-in installed by W3 Total Cache. Do not edit.
*/If the file is zero bytes, or the first twenty lines are garbage, or the file is full of base64-encoded blobs that look nothing like a plugin header, you have your answer. The dropin is the problem.
The fix
One line. From the cPanel user shell, in the document root:
mv wp-content/db.php wp-content/db.php.bakThat is it. Move the file aside (do not delete it; you may want to
look at it later), reload the site. WordPress will not find a
dropin, will fall back to the core wpdb connection logic, and
will connect to MySQL using the credentials in wp-config.php. The
error page disappears.
If the site still does not load, the problem is not the dropin. Go back to checking MySQL, the credentials, and the network path from the PHP process to the database socket. In our experience this is a roughly 90% hit rate fix for the specific symptom at the top of this post.
One caveat. If the site actually depends on the dropin (a
high-traffic site on Redis Object Cache, for instance) then moving
it aside fixes the connection error but leaves the site running
without its cache. Pages will load, possibly slowly. Once the site
is up, log into the WordPress admin, deactivate the cache plugin,
and reinstall it cleanly. A clean install writes a fresh db.php
and the site goes back to using the cache.
Why this happens: four common causes
We have walked into this in roughly equal proportions from four root causes. Each has a different prevention.
Partial-write plugin update. The most common. The site owner
clicks "update" on a caching plugin. The update copies a new
db.php into wp-content/. The copy is interrupted partway (disk
full, PHP timeout, opcache reload at the wrong moment) and the
resulting db.php is truncated. WordPress loads it on the next
request, hits a parse error, and the parse error surfaces as the
connection error because of where in core's boot sequence the
dropin loads.
Prevention: monitor free space on the partition holding /home,
monitor PHP-FPM error logs, and never run WordPress auto-updates
without a disk-space precondition.
Broken file ownership. Someone runs a sweeping chown on the
account, possibly during a server migration, possibly while trying
to "fix permissions" after a different issue. The dropin ends up
owned by root:root or nobody:nobody. PHP-FPM, running as the
cPanel user, cannot read it.
Prevention: any chown on a cPanel home directory uses the form
chown -R goldenvi:goldenvi /home/goldenvi/public_html/, preceded
by a quick ls -la to confirm the current owner is what you
expect.
Manual edit by a well-meaning third party. A developer with
SFTP access decides to "improve performance" by editing db.php
directly. They add a comment, save with a different line ending,
or break a quote. The file no longer parses.
Prevention: tell every developer with access that dropins are not to be edited by hand. If a caching plugin needs configuration, configure it through its admin UI. The dropin is generated by the plugin and overwritten on update; any hand edit is going to be lost or going to break.
Stale backup restore. A backup brings back an old db.php
whose version no longer matches the currently installed plugin.
The dropin tries to call into a runtime that has changed and the
load fails.
Prevention: after restoring wp-content, run the cache plugin's
"reinstall dropin" routine (most plugins expose one) or just
deactivate and reactivate the cache plugin to force a fresh write.
The 60-second check, summarised
For when you bookmark this and come back at 02:30 mid-incident:
# 1. confirm MySQL is fine
mysql -u root -p -e 'SELECT 1;'
# 2. confirm the dropin exists and look at it
ls -la wp-content/ | grep db.php
stat wp-content/db.php
head -20 wp-content/db.php
# 3. move it aside, reload the site
mv wp-content/db.php wp-content/db.php.bakIf step 3 fixes the site, the dropin was the problem and you have
a db.php.bak to inspect later. If not, the dropin was a red
herring; restore the file and debug MySQL, credentials, and the
socket path properly.
For the broader pattern of WordPress symptoms that point at one
layer and resolve at another, the
three real WordPress compromises
postmortem is worth a read; two of the three cases there started
with a symptom that pointed at the database layer and resolved
elsewhere. For a tighter reference on wp-content ownership and
which files are safe to chmod, the
wp-content permissions quick reference
has the canonical table.
How ServerGuard handles this
This is one of our WordPress-hardening use cases.
Detection. ServerGuard runs a scheduled
file-integrity check across every managed WordPress install's
wp-content/ directory. The check includes a specific routine for
the four dropin file names (db.php, object-cache.php,
advanced-cache.php, maintenance.php). For each dropin present,
the platform records the file's hash, ownership, and the plugin
header from the first twenty lines. On every subsequent scan, any
of three conditions raises a Safe-tier alert: the file's hash
changed without a corresponding plugin update event, the file's
owner changed away from the expected cPanel user, or the file's
header no longer matches a known caching plugin's signature (which
is the signal for a hand edit or a stale restore).
Detection. ServerGuard correlates "Error establishing a database connection" patterns in the WordPress error log or in the PHP-FPM error log against MySQL service state. When the WordPress error appears but MySQL is up, healthy, and serving other accounts, the platform raises a Safe-tier alert that specifically names the dropin as the likely cause. A connection error against an up MySQL is a much narrower problem than a connection error in general.
Remediation, Moderate tier, requires approval, upcoming
roadmap. Renaming a corrupted dropin to db.php.bak as the
first-line recovery action is a Moderate-tier action routed through
the Telegram or web approval flow. The on-call engineer sees the
exact file, the rename target, and which of the three detection
signals fired. The approval flow itself ships today; the catalogue
of dropin-related approvable actions is upcoming.
Out of scope. ServerGuard does not modify WordPress files autonomously. Even when the dropin is clearly broken, the rename requires a human approval, because the dropin may be load-bearing for a high-traffic site and the consequence of moving it aside on a Redis-backed site is a measurable performance change. That is a decision a human should make per site. The platform does not reinstall the plugin that owns the dropin either.
The point is the same as the point of the post: next time
WordPress says the database connection is down and MySQL is clearly
up, the right first check is not the database. It is
ls -la wp-content/ | grep db.php. We forget that. The platform
should not.
مقالات ذات صلة
- قراءة 6 دقيقة
When you have to suspend a WooCommerce client: anatomy
Anatomy of a forced suspension on a shared cPanel server The decision to take a paying client offline to protect fourteen other paying clients is the worst part of running a small hosting agency. There is no scripted version of it that feel
- قراءة 14 دقيقة
Patchman activation breaks PHP sites: memory_limit gotcha
Patchman activation breaks PHP sites: memorylimit gotcha The ticket landed mid-morning. Thirteen WordPress sites on were intermittently returning 500s. Not all at once, not on a clean five-minute beat, not correlated with traffic. The sites
- قراءة 16 دقيقة
Three real WordPress compromises and how we found them
Three real WordPress compromises and how we found them This post is for the people who manage ten to fifty WordPress sites on one or two cPanel boxes, do not have a dedicated security engineer, and keep finding that the malware scanner subs