WP Tuts Hub

Practical WordPress tutorials for developers
Advertisement

Browse

  • Home
  • Categories
    • WordPress & AI
    • WordPress Security
    • WordPress Troubleshooting
    • WordPress Tips and Tricks
    • SSH (Secure Shell)
    • WordPress Insights
    • WordPress Common Facts
  • Series Tutorials
    • Advanced WordPress Topic
  • Privacy Policy
  • Terms of Use
  • Contact Me
  • About WP Tuts Hub
Home/WordPress Troubleshooting/Fix WordPress Database Connection Error
WordPress Troubleshooting
November 11, 2026

Fix WordPress Database Connection Error

  • MySQL
  • wp db repair
  • WP_DEBUG
  • WP-CLI
  • wp-config.php
Fix WordPress Database Connection Error

When a site drops to a bare page reading Error establishing a database connection, WordPress has already given up: it never reached the point where it could load a theme, so the screen you see is the last thing PHP could print. The quickest way to fix a WordPress database connection error is to stop reading that screen and start reading the four constants that produced it. Below are the three realistic routes to those constants, when each one fits, and a full build of the one that hands you the real MySQL error instead of a generic failure.

Three routes to the same four constants, and only one of them tells you why

Every fix ends in the same place: a working DB_NAME, DB_USER, DB_PASSWORD and DB_HOST in wp-config.php. What separates the approaches is how much information you get on the way there.

Route 1: editing wp-config.php through the hosting file manager

This is the default on shared hosting. You open the file manager, download wp-config.php, compare the four constants against the database section of the control panel, and upload again. It fits when you have no SSH access and no WP-CLI, and it is genuinely the right call when you already know the password was rotated and the config file was never updated.

The weakness is that it is guesswork. A wrong password, a wrong host, a missing database and a crashed MySQL server all produce the same screen, so you end up editing four values hoping one of them was the culprit. Every save is also a chance to introduce a PHP syntax error on top of the original fault.

Route 2: WP-CLI and mysqladmin from a shell

With SSH access you can read the constants without touching the file, ask the MySQL client whether the host answers at all, and repair tables from the same prompt. It fits any host that gives you a terminal, and it is the only route that produces output you can paste into a support ticket.

Its weakness is that it tests the credentials you type, not the ones WordPress is using. Mistype the password at the prompt and you learn nothing about the site.

Route 3: a diagnostic mu-plugin that prints the raw MySQL error

A must-use plugin loads before regular plugins and can register its own WP-CLI command. That command opens a raw mysqli connection using the exact constants from wp-config.php and prints the error code MySQL returns. It fits when you have file access but no shell, and it is the only route that separates access denied from unknown database from can’t connect through socket.

Recommended combination: read the constants with WP-CLI, confirm the host with mysqladmin, then run the probe from route 3 to capture the exact error. The rest of this article builds that in order.

Read the four constants before you change a single character

Open wp-config.php and check the database block character by character. The most common causes of this error are a password with a trailing space pasted from the control panel, a database name missing its host prefix, and a DB_HOST that says localhost when the host requires 127.0.0.1 or a socket path.

<?php
/** The database name exactly as the control panel spells it, prefix included. */
define( 'DB_NAME', 'example_wp' );

/** The database user, not the control panel login. */
define( 'DB_USER', 'example_wpuser' );

/** The generated password, copied without a trailing space. */
define( 'DB_PASSWORD', 'kR7p...generated...' );

/** 'localhost' uses the socket; '127.0.0.1' forces TCP. They are not interchangeable. */
define( 'DB_HOST', 'localhost' );

/* Non-standard port or socket: use one of these instead. */
// define( 'DB_HOST', '127.0.0.1:3307' );
// define( 'DB_HOST', 'localhost:/var/run/mysqld/mysqld.sock' );

Reading the file is only half the job, because a host can inject constants from elsewhere or a stray wp-config.php can sit one directory up. The wp config commands parse the file without booting WordPress, so they still answer when the front end is dead.

# Read the values WordPress is actually loading, no editor required.
wp config get DB_NAME
wp config get DB_USER
wp config get DB_HOST

# Dump everything the file defines, in case a host injects constants elsewhere.
wp config get --json

The three database constants read straight from wp-config.php without opening the file (test site: WordPress 7.1.3, PHP 8.2):

wordpress
wp
localhost:/run/mysqld/mysqld.sock

If wp config get returns a value that differs from what the control panel shows, the file is not the one you think it is. If it returns nothing at all, the constant is undefined and WordPress is falling back to a default that will never work.

Test the host with mysqladmin before you blame WordPress

mysqladmin is the decisive test because it needs no WordPress, no PHP and no theme. It answers one question: does this host, port and credential combination reach a live MySQL server? Run it with the same values you just read out of the config file.

# Does MySQL answer on the TCP port?
mysqladmin --host=127.0.0.1 --port=3306 --user=example_wpuser --password ping

# Same test over the socket that 'localhost' resolves to.
mysqladmin --socket=/var/run/mysqld/mysqld.sock --user=example_wpuser --password ping

Read the reply literally:

  • mysqld is alive — the server is up and the credentials are accepted. The fault is inside WordPress: a stale wp-config.php, an object cache pointing at a dead Redis instance, or a corrupted table.
  • Access denied for user — the password or username is wrong, or the account is not granted rights on that database from that host.
  • Can’t connect to MySQL server … (111) — nothing is listening. The database service is down or a firewall blocks the port.
  • Unknown MySQL server host — DB_HOST is a typo or a hostname that no longer resolves.

Omitting --password entirely makes the client prompt for it, which keeps the secret out of your shell history. That matters on a shared server where other users can read the process list.

Build the probe that prints the MySQL error WordPress hides

WordPress deliberately replaces the driver error with a friendly page, so the code that would tell you what happened is thrown away. This must-use plugin registers a wp dbdoctor command that opens its own connection with the constants from wp-config.php and prints whatever MySQL says. Because it opens a second connection, it reports the failure even while WordPress itself is loading normally.

<?php
/**
 * Plugin Name: Database Connection Doctor
 * Description: Probes the credentials in wp-config.php and prints the raw MySQL error.
 */

class WPTuts_DB_Doctor {

	/**
	 * Open a raw mysqli connection using the constants from wp-config.php.
	 *
	 * ## OPTIONS
	 *
	 * [--db-host=<host>]
	 * [--db-user=<user>]
	 * [--db-password=<password>]
	 * [--db-name=<name>]
	 *
	 * ## EXAMPLES
	 *
	 *     wp dbdoctor probe
	 *     wp dbdoctor probe --db-name=missing_db
	 */
	public function probe( $args, $assoc_args ) {
		$host     = isset( $assoc_args['db-host'] ) ? $assoc_args['db-host'] : DB_HOST;
		$user     = isset( $assoc_args['db-user'] ) ? $assoc_args['db-user'] : DB_USER;
		$password = isset( $assoc_args['db-password'] ) ? $assoc_args['db-password'] : DB_PASSWORD;
		$name     = isset( $assoc_args['db-name'] ) ? $assoc_args['db-name'] : DB_NAME;

		$target = $this->parse_host( $host );

		WP_CLI::log( 'DB_HOST     : ' . $host );
		WP_CLI::log( 'Host        : ' . $target['host'] );
		WP_CLI::log( 'Port/socket : ' . $target['label'] );
		WP_CLI::log( 'DB_USER     : ' . $user );
		WP_CLI::log( 'DB_NAME     : ' . $name );
		WP_CLI::log( '' );

		mysqli_report( MYSQLI_REPORT_OFF );

		try {
			$db = new mysqli( $target['host'], $user, $password, $name, $target['port'], $target['socket'] );
		} catch ( Throwable $e ) {
			WP_CLI::error( 'Connection failed: ' . $e->getMessage(), false );
			return;
		}

		if ( $db->connect_errno ) {
			WP_CLI::error( sprintf( 'Connection failed (%d): %s', $db->connect_errno, $db->connect_error ), false );
			return;
		}

		WP_CLI::success( 'Connected to ' . $name . ' via ' . $db->host_info . ' (server ' . $db->server_info . ')' );
		$db->close();
	}

	private function parse_host( $host ) {
		$port   = null;
		$socket = null;
		$label  = 'default';

		if ( false !== strpos( $host, ':' ) ) {
			list( $host, $suffix ) = explode( ':', $host, 2 );

			if ( ctype_digit( $suffix ) ) {
				$port  = (int) $suffix;
				$label = 'port ' . $port;
			} else {
				$socket = $suffix;
				$label  = 'socket ' . $socket;
			}
		}

		if ( '' === $host ) {
			$host = 'localhost';
		}

		return array(
			'host'   => $host,
			'port'   => $port,
			'socket' => $socket,
			'label'  => $label,
		);
	}
}

if ( defined( 'WP_CLI' ) && WP_CLI ) {
	WP_CLI::add_command( 'dbdoctor', 'WPTuts_DB_Doctor' );
}

Save it as wp-content/mu-plugins/db-connection-doctor.php. The parse_host() helper matters more than it looks: WordPress accepts host:port and host:/path/to/socket in DB_HOST, and a probe that passes the whole string to mysqli would fail for the wrong reason.

# Happy path: confirms the constants, the socket and the server version.
wp dbdoctor probe

# Force a specific failure to see the code WordPress hides.
wp dbdoctor probe --db-name=definitely_not_a_database
wp dbdoctor probe --db-user=example_wpuser --db-password=wrong

The probe connects with the constants WordPress is using and reports the socket, host and server version (test site: WordPress 7.1.3, PHP 8.2):

DB_HOST     : localhost:/run/mysqld/mysqld.sock
Host        : localhost
Port/socket : socket /run/mysqld/mysqld.sock
DB_USER     : wp
DB_NAME     : wordpress

Success: Connected to wordpress via Localhost via UNIX socket (server 11.8.6-MariaDB-0+deb13u1 from Debian)

Forcing a bad database name surfaces the raw MySQL error code that the front-end screen hides (test site: WordPress 7.1.3, PHP 8.2):

DB_HOST     : localhost:/run/mysqld/mysqld.sock
Host        : localhost
Port/socket : socket /run/mysqld/mysqld.sock
DB_USER     : wp
DB_NAME     : definitely_not_a_database

[11-Nov-2026 01:28:12 UTC] PHP Warning:  mysqli::__construct(): (HY000/1044): Access denied for user 'wp'@'localhost' to database 'definitely_not_a_database' in /var/www/html/wp-content/mu-plugins/db-connection-doctor.php on line 42
Warning: mysqli::__construct(): (HY000/1044): Access denied for user 'wp'@'localhost' to database 'definitely_not_a_database' in /var/www/html/wp-content/mu-plugins/db-connection-doctor.php on line 42
Error: Connection failed (1044): Access denied for user 'wp'@'localhost' to database 'definitely_not_a_database'

An error 1045 means the credentials are wrong. An error 1049 means the credentials are fine but the database name does not exist. An error 2002 means the socket path is wrong. Each one points at a different line of wp-config.php, which is exactly the distinction the front-end screen withholds.

If wp itself refuses to start, the probe cannot run either — WordPress has to boot before a mu-plugin command exists. That is the moment to fall back to mysqladmin, which needs nothing but a shell.

Turn on WP_DEBUG_LOG so the failure leaves a written record

Once you can reach the dashboard again, switch on logging before you start changing anything. A written record turns a one-off glitch into a pattern you can act on, and it catches the intermittent cases where the site loads fine every time you look at it.

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );        // writes to wp-content/debug.log
define( 'WP_DEBUG_DISPLAY', false );   // keeps errors out of the page
@ini_set( 'display_errors', 0 );

Add those lines above the /* That's all, stop editing! */ marker, reload the broken page, then read wp-content/debug.log. The empty result is as informative as a full one: if the log stays at zero bytes after a failed request, the request died before WordPress could log anything, which points squarely at the connection layer rather than at a plugin. If the log fills with a fatal from a single plugin, you have a different problem wearing the same costume.

Turn WP_DEBUG_DISPLAY back off when you are finished. Leaving it on in production prints file paths and query fragments onto public pages.

Repair a corrupted table with wp db check and wp db repair

If mysqladmin ping answers and the constants are correct, the remaining suspect is storage. A table marked as crashed makes the connection handshake fail in ways that look identical to bad credentials. Both commands need a working connection, which is why they come last.

# Report the status of every table in the database.
wp db check

# Ask MySQL to repair anything it can.
wp db repair

# Narrow it down to one table when the list is long.
wp db query "CHECK TABLE wp_posts"

Expect a caveat on modern installs: wp db repair only does real work on MyISAM and Aria tables. InnoDB reports The storage engine for the table doesn’t support repair, which is not a failure message — it means the engine has no repair path and you should restore the affected table from a backup, or export and re-import the database with wp db export and wp db import. Running the repair anyway is still worth ten seconds, because it confirms which engine each table uses.

When the credentials are right and the connection still drops

A site that connects and then fails under load is a different fault. Check three things in order. First, whether the host caps concurrent connections: SHOW STATUS LIKE 'Threads_connected' against max_connections shows whether you are hitting a ceiling. Second, whether DB_HOST should be 127.0.0.1 rather than localhost — the first forces TCP, the second uses a socket, and hosts that run MySQL in a separate container only expose one of them. Third, whether a persistent object cache is still pointing at a Redis or Memcached instance that has been removed; the drop-in in wp-content/object-cache.php fails before WordPress ever reaches MySQL.

Once the site is stable, the follow-up worth doing is adding mysqladmin --host=127.0.0.1 --user=... --password ping to whatever already watches your uptime. It answers in under a second, needs no WordPress, and tells you whether the next outage is a database problem or an application one before you open a ticket.

Share this:

  • Share on X (Opens in new window) X
  • Share on Facebook (Opens in new window) Facebook
  • Share on LinkedIn (Opens in new window) LinkedIn
  • Share on WhatsApp (Opens in new window) WhatsApp
  • Share on Reddit (Opens in new window) Reddit
  • Email a link to a friend (Opens in new window) Email

Related

Previous Article No Shipping Options Available in WooCommerce: Fix It

About the author

Md. Zubaer Ahammed
Md. Zubaer Ahammed

Md. Zubaer Ahammed is a senior full-stack developer building learning platforms and modern web systems with WordPress, Laravel, Python/Django, Vue, and React. More at https://zubaer.com/.

Related Posts

  • Secure WordPress site from hacking, DDoS and Brute force attack How to Secure WordPress Site From Hacking, DDoS and Brute Force Attack
  • Update WordPress site URL via PHPmyadmin How to Replace Old URLs After a WordPress Domain Change
WP Tuts Hub

WordPress tutorials, themes, and plugins — written for builders.

  • Portfolio
  • Calorie Calculator Pro
  • Contact
  • About
  • Privacy
  • Terms

© 2026 WP Tuts Hub · Md. Zubaer Ahammed