or several weeks I would dread checking up on my site for fear of the 'Error establishing database connection' prompt in bold letters on a blank white page. I'll share my journey to a solution for those dealing with the same issue...
To be frank, I'm not a big fan of WordPress. It's a leviathan of a framework that has more break-points and vulnerabilities than I care to think about, but it's convenient. I built my site on it because I didn't want to have to write a sophisticated CMS from scratch. Generally, It's come in handy, but while I was plagued by the dreaded and mysterious 'Error establishing database connection,' its complexity was the bane of my existence.
The truth is, this error can be caused by a wide range of issues with your WordPress install, I'll do my best to address them as thoroughly as possible. The article is written from the perspective of a DigitalOcean Ubuntu install, but should apply generally to any linux host with root access.
So what causes this issue, and why is it so difficult to diagnose? In most cases, my own included, the database becomes disconnected due to MySQL running out of memory, but that is rarely the root of the issue. You probably should be running WordPress on a host with at least 1GB of ram, but unless you have heavy traffic, 512MB will do. So don't jump straight into upgrading your host before exploring the issue further.
Finding the Failure
The first thing you should test for is a corrupted database. The best way to do this is to simply start/restart the MySQL service. The following commands should work on most linux servers:
# check if MySQL is running:
service mysql status
# to start:
service mysql start
# to restart:
service mysql restart
If MySQL fails to start, or the 'Error establishing database connection' prompt persists, reboot the system and check again for good measure:
# reboot server:
reboot
Should the problem continue, it likely means that your database has been corrupted, but fear not, it's (usually) an easy fix.
Go to your wp_config.php file and place the following line of code at the bottom:
define('WP_ALLOW_REPAIR', true);
Next, navigate to http://yourdomain.com/wp-admin/maint/repair.php, from there select 'repair and optimize database' and let it do it's thing.
CAUTION: one does not need to be logged in to access this utility, so once you have repaired your database, make sure that you remove the line listed above from your wp_config.php file.
At this point, go ahead and pat yourself on the back, but don't drop your guard quite yet. In most cases, this is just a bandage on an untreated wound.
In any situation, repairing and optimizing the database should be a part of regular maintenance. If you don't want to do this manually, there's a nifty plugin called WP-DBManager that is easy to configure and handles the job for you.
However, this did not solve the problem. I still would run into the 'Error establishing database connection' prompt on a weekly basis. At this point I began to dig around the php.ini configuration file. I don't advise you do this unless you really know what you're doing in there. There's a lot that can be done from there, but none of it rendered a solution, so I won't get into it.
My next move was to upgrade my hosting to 2GB / 2CPU. This didn't cut it either. I was perplexed, and on the verge of rebuilding my site from scratch when I though to check the network logs. My bandwidth usage wasn't out of the norm, so I didn't think to look into it before, but that is where I found the root of the issue...
My site was being regularly hit with DDoS attacks leveraging the WordPress pingback functionality. They are sent in the form of XML-RPC requests; WordPress uses it to remotely execute funtions, widely used by plugins. They are pretty easy to identify in the network logs, containing something along the lines of 'POST /xmlrpc.php HTTP/1.0'.
To check the log from the console:
# if Apache:
grep xmlrpc /var/log/apache2/access.log
# if Nginx:
grep xmlrpc /var/log/nginx/access.log
If you're seeing a lot of entries with this 'POST /xmlrpc.php HTTP/1.0' coming from the same, or similar IP addresses, then it's quite likely that you're site has suffered a DDoS attack via WordPress' very own pingback functionality.
From my research, most attacks of this sort are originating from IPs in the range of 185.130.5.* belonging to some VPN hosting service ohs4you.net with an 'abuse' contact of abuse@ohs4you.net. It looked sketchy so I didn't bother.
Securing a Solution
The most effective approach would be to block XML-RPC traffic server-side. However, many plugins utilize this type of traffic, so while I will explain how this is done, I advise that you explore the other solutions first.
This first step is optional, but it helps prevent such attacks from bogging down your site. Disable pingbacks. From the dashboard go to Settings >> Discussion and tick the box labeled 'Allow link notifications from other blogs...'. This isn't a cure, but it can help reduce strain on server resources and prevent your site from becoming complicit in such attacks.
There are a couple things that can be done next, most of which should work on their own, but feel free to employ a combination of them.
The White-List Method
The most common, and probably the most simple solution is to install the Jetpack plugin made by WordPress. Enable the 'Protect' add-on, aaaaannnnnd... done, mostly. You could go in and set up the white-list if certain services and/or devices need XML-RPC traffic enabled, but since doing so is particularly install-specific, I won't go further.
If you don't want to install such a heavy plugin, the same can be done manually by editing your .htaccess file. Say you want the IPs 124.124.8.16&124.124.8.32 to have XML-RPC acces, just include the following in your .htaccess file:
<FilesMatch "xmlrpc.php$">
order deny,allow
deny from all
allow from 124.124.8.16
allow from 124.124.8.32
</FilesMatch>
Or you could create your own plugin to specifically filter the pingback traffic:
add_filter( 'xmlrpc_methods', function( $methods ) {
unset( $methods['pingback.ping'] );
return $methods;
});
This will still allow the traffic through, but won't allow the attacks to drain up as much of your precious server juices.
The Security-Plug Method
My personal preference for WordPress security is AIOWPS. There are a TON of configuration options, so I won't go into much detail on configuration beyond our purposes, but I suggest exploring all of the options and what they do.
Just make sure you don't secure file permissions until AFTER you have completed all of your plugin configuration needs (if you did your server side user and install setup correctly, it will prevent edits to config and .htaccess files, and not all plugins will warn you of this).
From the dashboard go to WP security >> Firewall and check the box labeled 'Enable Pingback Protection'. NOTE: this will affect plugins that use XML-RPC traffic, so if you run anything relies on such traffic, refer to the previous method.
Another thing you could do with the AIOWPS plugin is blacklist IPs. If your attacks, like mine, originate from a very specific range of IPs, then you can blacklist that range. I blacklisted 185.130.5.* to prevent traffic from all servers with that IP prefix. Since all of those IPs belonged to a hosting company I deemed it a safe approach. Whether you block the individual IP addresses or a range is case dependent and is up to your digression.
The DNS Method
This approach can be a little more involved, but has the potential to benefit your site far beyond the scope of your current issue. Look into changing your DNS provider to CloudFlare. I plan to talk about how awesome CloudFlare is in a future post, so I won't spoil much here.
I made the change while configuring the W3 Total Cache plugin and am incredibly pleased with the performance improvements they offer.
Back on topic, CloudFlare offers a wide range of security options that are implemented by default, and many more that you likely will never need to play with.
I made this change after configuring other solutions, so I can't speak directly to its effectiveness at preventing DDoS via pingback, but many others have praised it as a robust solution.
I suggest setting it up just for the sake of performance and poking around the settings a little bit. You would be amazed how much they offer for free.
The Quick Method ~ [Apache Only]
Maybe you just want a simple command to qualm your woes, you can easily block all XML-RPC traffic from the server.
If your server's OS is based on Debian and you're using Apache, then this can be done using the a2enconfig script (the a2 stands for Apache 2). The command to disable XML-RPC traffic:
# works for most debian-based servers
a2enconf block-xmlrpc
Whether or not this works first try, you should update everything for the sake of security:
# fetch updates:
apt-get update
# install fetched updates:
apt-get install
This should suffice, but it's safe to version check vital installs like PHP, Apache, and MySQL. This is worth another guide, so I'll leave it up to your googling skills...
The All-Out Method
If all else fails, hopefully it hasn't, then you can manually block all XML-RPC traffic from the server's config files... To open those files from the console:
# if Apache
nano /etc/apache2/sites-available/000-default.conf
# if Nginx
nano /etc/nginx/sites-available/yourURL.com
Then add the highlighted lines as applies:
# if Apache
<VirtualHost>
# stuff
<files xmlrpc.php>
order allow,deny
deny from all
</files>
</VirtualHost>
# if Nginx
server {
# stuff
location /xmlrpc.php {
deny all;
}
}
This is the most direct solution, but not the first you should jump to. NOTE: the drawback to this and the previous approach is that nothing, including WordPress and its plugins, can use XML-RPC traffic. This might be fine in your case, but it could also break some functionality, so be vigilant.
That's pretty much the gist of it. The dreaded 'Error establishing database connection' should be a thing of the past... If problems persist, or you are having trouble implementing one of the methods, you're welcome to get in touch and I'll try to provide context-specific advice.
For some further reading on the issue, check out: