MySQL Error 1045 means the server rejected your login. Learn the real causes behind access denied and how to fix each one on a WordPress site.
If you have ever seen ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES) in a terminal, or watched your WordPress site fall over with an "Error establishing a database connection" message, you have run into MySQL error 1045. It is one of the most common database errors, and it comes down to a single idea: MySQL looked at the username, password, and host you connected with, and none of it matched an account it trusts.
The frustrating part is that the message rarely tells you which piece is wrong. The same error covers a mistyped password, a user that only exists for a different host, a missing privilege, and an authentication method your PHP version can't speak. This guide breaks the error into its real causes, shows you how to tell them apart, and walks through the exact fix for each on a typical WordPress stack. The commands assume MySQL 8.0 or later or a recent MariaDB; where the two differ, that is called out.
TL;DR
- Error 1045 means the
user+host+passwordcombination you supplied doesn't match any row in themysql.usertable.- Read the exact text: the
'user'@'host'part tells you which host MySQL tried to match, and(using password: YES/NO)tells you whether a password was even sent.- For WordPress specifically, the usual culprit is wrong credentials in
wp-config.php(DB_USER,DB_PASSWORD,DB_HOST) after a migration or password change.- Common root causes: wrong password, wrong host entry, missing user or privileges, forgetting
FLUSH PRIVILEGESafter manual edits, and thecaching_sha2_passwordvsmysql_native_passwordmismatch introduced in MySQL 8.0.- Resetting a forgotten root password means restarting MySQL with
--skip-grant-tables, which is insecure and must be undone immediately afterward.
Every MySQL account is a combination of a username and a host, written as 'user'@'host'. When you try to connect, MySQL doesn't just check the password. It looks for a row in the internal mysql.user table where the username matches, the host you're connecting from matches, and the password (and authentication method) line up. If any of those three fail, you get error 1045.
The error text carries more information than it first appears:
ERROR 1045 (28000): Access denied for user 'wpuser'@'<blog.example.com>' (using password: YES)
'wpuser'@'<blog.example.com>' is the exact user and host MySQL tried to match. If you expected localhost and see a hostname or IP instead, the host is the problem.(using password: YES) means a password was sent but rejected. (using password: NO) means no password was sent at all, which usually points to an empty DB_PASSWORD or a missing -p flag.That single line tells you where to start looking, so read it carefully before changing anything.
Several of the fixes below change user accounts or touch the mysql system database. On a production WordPress site, take a backup before you make account or privilege changes, so you can roll back if a fix has an unintended effect on other applications sharing the same server.
If your site runs on MagicWP, you can trigger an on-demand backup from the dashboard before touching anything, and restore with one click if needed. On a self-managed server, export the database and copy wp-config.php somewhere safe first.
Important: Editing MySQL user accounts affects every application on that server, not just WordPress. On a shared database server, confirm you aren't about to lock out another site.
This is the most frequent cause, and the first thing to rule out. The password stored for the account simply doesn't match the one you supplied.
Test it directly at the command line, outside of WordPress, so you're checking MySQL alone:
mysql -u wpuser -p
Type the password when prompted. If you still get 1045, the password is wrong or the account has a different host entry (see Cause 2). Avoid putting the password directly after -p on the command line; it can be exposed in your shell history and process list.
If you have working root or admin access, reset the account's password. On MySQL 8.0+ and MariaDB 10.2+:
ALTER USER 'wpuser'@'localhost' IDENTIFIED BY 'a-new-strong-password';
FLUSH PRIVILEGES;
Then update wp-config.php to match, so WordPress uses the new value:
/** Database username */
define( 'DB_USER', 'wpuser' );
/** Database password */
define( 'DB_PASSWORD', 'a-new-strong-password' );
A subtle version of this problem: the password is correct but contains characters like $, `, or \ that your shell or a config file interpreted before MySQL ever saw them. If a password looks right but keeps failing, wrap it in single quotes where you use it and consider regenerating it without shell-special characters.
MySQL treats 'wpuser'@'localhost' and 'wpuser'@'%' as two different accounts, even though the username is identical. If your application connects from a host that no account covers, you get 1045 no matter how correct the password is.
First, confirm which host entries actually exist. Log in as root or an admin user and run:
SELECT user, host FROM mysql.user WHERE user = 'wpuser';
You might see only wpuser / localhost, while your app connects over TCP as 127.0.0.1 or from a separate application server's IP. Those don't match.
There is a related gotcha specific to how you connect. Using localhost tells the MySQL client to use a Unix socket, while 127.0.0.1 forces a TCP connection. They can resolve to different account entries and behave differently:
# Uses a Unix socket, matches 'wpuser'@'localhost'
mysql -u wpuser -p -h localhost
# Forces TCP, may match 'wpuser'@'127.0.0.1' or fail
mysql -u wpuser -p -h 127.0.0.1
For WordPress, this is controlled by DB_HOST in wp-config.php. On most single-server setups the correct value is localhost; some managed hosts require 127.0.0.1 or a specific hostname or socket path instead.
/** Database hostname */
define( 'DB_HOST', 'localhost' );
If the account genuinely needs to accept connections from another host, create the matching entry with the privileges it needs. Grant access only from the specific host that requires it rather than opening it to every host:
CREATE USER 'wpuser'@'<198.51.100.5>' IDENTIFIED BY 'a-new-strong-password';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wpuser'@'<198.51.100.5>';
FLUSH PRIVILEGES;
Opening a database user to 'wpuser'@'%' (any host) works but widens your attack surface. Prefer a named host or IP unless you have a specific reason not to.
If the account was never created, or was created but has no rights on the database WordPress is trying to use, MySQL returns 1045. This often shows up right after a migration, where the database was imported but the user wasn't recreated on the new server.
Check whether the user exists at all:
SELECT user, host FROM mysql.user WHERE user = 'wpuser';
If it returns no rows, create the account and grant it access to just the WordPress database, following the principle of least privilege. WordPress does not need ALL PRIVILEGES on every database on the server; scope it to its own:
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'a-new-strong-password';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wpuser'@'localhost';
FLUSH PRIVILEGES;
If the user exists but you suspect it's missing rights, inspect what it currently has:
SHOW GRANTS FOR 'wpuser'@'localhost';
Grant the WordPress database specifically if it's absent from the output. Standard WordPress operation needs read and write data access plus the ability to change table structure during updates, which ALL PRIVILEGES on the single site database covers comfortably without giving server-wide power.
If you changed an account by running a manual UPDATE against the mysql.user table, rather than using ALTER USER or GRANT, MySQL will keep using the old, cached grant data until you tell it to reload:
FLUSH PRIVILEGES;
This is an easy step to forget, and it produces a confusing situation where the table clearly shows the right values but logins still fail with 1045. As a rule, prefer ALTER USER and GRANT over direct table edits; they apply immediately and are far less error-prone. Reserve manual UPDATE statements for recovery situations, and always follow them with FLUSH PRIVILEGES.
MySQL 8.0 changed the default authentication method from mysql_native_password to caching_sha2_password. This is more secure, but older clients, some PHP versions, and certain database libraries can't negotiate it. When that happens you may see error 1045 or the closely related "client does not support authentication protocol" message, even with a correct password.
Check which plugin an account uses:
SELECT user, host, plugin FROM mysql.user WHERE user = 'wpuser';
If the client genuinely can't support caching_sha2_password, switch that account to the older method as a compatibility measure:
ALTER USER 'wpuser'@'localhost' IDENTIFIED WITH mysql_native_password BY 'a-new-strong-password';
FLUSH PRIVILEGES;
Treat this as a fallback, not a default. The newer method exists for good reasons, so the better long-term fix is usually to update the PHP version or database driver so it can speak caching_sha2_password. Modern, supported versions of PHP and WordPress handle it without trouble, which is one reason keeping your stack current quietly prevents this class of error.
Note: MariaDB does not use
caching_sha2_passwordand handles authentication plugins differently, so this specific mismatch is a MySQL 8.0+ concern. If you're on MariaDB, focus on the earlier causes instead.
If the account you've lost access to is root itself, you can't simply log in and run ALTER USER. The recovery path is to restart the server in a mode that skips authentication, reset the password, then restart normally. Because this mode grants anyone full access, do it only when necessary and undo it immediately.
This procedure follows the official MySQL approach. The exact service commands vary by operating system and init system, so adjust them to your environment.
Step 1 — Stop the MySQL server.
sudo systemctl stop mysql
Step 2 — Start it with authentication skipped.
With --skip-grant-tables, MySQL lets anyone connect without a password and with all privileges, and it disables account-management statements. As of MySQL 8.0, this option also automatically enables skip_networking, which blocks remote connections while you're in this state.
sudo mysqld_safe --skip-grant-tables --skip-networking &
Step 3 — Connect as root without a password.
mysql -u root
Step 4 — Reload the grant tables, then reset the password.
Because account-management statements are disabled in skip-grant mode, reload the grant tables first so ALTER USER will work:
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'a-new-strong-root-password';
FLUSH PRIVILEGES;
EXIT;
Step 5 — Restart MySQL normally.
Stop the skip-grant instance and start the service the normal way, without --skip-grant-tables and without skip_networking:
sudo mysqladmin shutdown
sudo systemctl start mysql
You should now be able to log in with the new root password. Confirm the server is no longer running in skip-grant mode before you consider the job done; leaving it in that state is a serious security hole.
Important: While MySQL is running with
--skip-grant-tables, the server is effectively wide open to anyone who can reach it. Keep this window as short as possible and don't expose the server to the network during it.
Rather than trying fixes at random, let the error text and a couple of quick checks point you to the right one.
| Symptom | Likely cause | First check |
|---|---|---|
(using password: NO) |
Empty or missing password | Check DB_PASSWORD in wp-config.php, or that you passed -p |
| Correct password still rejected | Host mismatch | SELECT user, host FROM mysql.user WHERE user = 'wpuser'; |
| No rows for the user | User doesn't exist | Create the user, grant on the site database |
| Table shows right values, login still fails | Grants not reloaded | Run FLUSH PRIVILEGES; |
| "Client does not support authentication protocol" | Plugin mismatch (MySQL 8.0+) | Check the plugin column for that user |
| Can't log in as root at all | Forgotten root password | Reset via --skip-grant-tables |
On a live WordPress site, you often don't see error 1045 directly. Instead you see "Error establishing a database connection" in the browser, because WordPress catches the failure and shows a friendlier message. The underlying cause is frequently 1045, and it usually traces back to wp-config.php.
The three constants that matter are DB_USER, DB_PASSWORD, and DB_HOST. After a migration, a host change, or a password rotation, one of these is often stale:
/** Database username */
define( 'DB_USER', 'wpuser' );
/** Database password */
define( 'DB_PASSWORD', 'a-new-strong-password' );
/** Database hostname */
define( 'DB_HOST', 'localhost' );
Confirm each value matches what actually exists in MySQL, test the same credentials directly with the mysql client to isolate WordPress from the equation, and only then start editing accounts. If the credentials work at the command line but WordPress still fails, the problem is in wp-config.php, not in MySQL.
On managed WordPress hosting, the database credentials are provisioned and wired into your site for you, so this class of error is far less common. On MagicWP, database access is available through a temporary phpMyAdmin session and the credentials are managed as part of the platform, which removes most of the manual wp-config.php juggling that produces 1045 in the first place. When you do need to inspect the database directly, you can reach it without hunting down credentials by hand.
(using password: YES) means a password was sent to the server but it didn't match the account, so focus on the password value or the host part of the account. (using password: NO) means no password was sent at all, which usually points to an empty DB_PASSWORD in wp-config.php, a missing -p flag on the command line, or a script that isn't passing credentials. The distinction narrows the cause immediately, so always read it first.
The most common reason is a host mismatch. MySQL accounts are defined as 'user'@'host', so 'wpuser'@'localhost' and 'wpuser'@'127.0.0.1' are separate accounts. If you connect from a host no account covers, the login fails regardless of the password. Check the existing entries with SELECT user, host FROM mysql.user WHERE user = 'wpuser'; and confirm your connection method (localhost uses a socket, 127.0.0.1 forces TCP).
Start with wp-config.php and verify DB_USER, DB_PASSWORD, and DB_HOST match a real MySQL account. Test those exact credentials with the mysql command-line client to confirm whether the problem is in WordPress or in MySQL. If the credentials fail at the command line too, reset the account password with ALTER USER and update wp-config.php to match. If they work at the command line but WordPress still fails, the config file is the problem.
Only briefly, and only when you have no other way in. While the server runs with --skip-grant-tables, anyone who can reach it has full access with no password, so it's a serious security risk. On MySQL 8.0 and later the option also disables networking automatically, which limits exposure, but you should still keep the window short and restart the server normally as soon as you've reset the password.
MySQL 8.0 changed the default authentication method to caching_sha2_password, and older clients or PHP versions may not support it. If a previously working account suddenly fails after an upgrade, check its plugin value. You can switch the account back to mysql_native_password for compatibility, but the better fix is to update the client, PHP version, or driver so it supports the newer, more secure method.
Not always. ALTER USER, CREATE USER, and GRANT apply their changes immediately, so an explicit FLUSH PRIVILEGES isn't required after them. You do need it if you edited the mysql system tables directly with an UPDATE statement, or when reloading grants inside a --skip-grant-tables session. When in doubt, running it is harmless.
MySQL error 1045 looks intimidating because one message covers several distinct problems, but the fix is systematic once you read the error carefully. The 'user'@'host' part and the (using password: YES/NO) note tell you where to start, and from there it's a matter of ruling out a wrong password, a host mismatch, a missing user or privilege, an unreloaded grant table, or the MySQL 8.0 authentication change. For WordPress sites, the trail almost always leads back to the database constants in wp-config.php.
Before changing any account on a production server, take a backup so you can undo an unintended effect, and prefer scoped, least-privilege grants over opening accounts to every host. Managed WordPress hosting removes most of these failure points by provisioning and wiring up database credentials for you; on MagicWP you can take an on-demand backup before any change and reach your database through a temporary phpMyAdmin session, so recovering from a database access problem rarely means hand-editing account tables at all.
Monthly engineering notes, product updates, and WordPress performance tips. No spam, unsubscribe anytime.