If an application stopped connecting after a move to MySQL 8.4, and the server says Plugin 'mysql_native_password' is not loaded, the account is still using the old SHA-1 based login method and 8.4 ships with it switched off. You have two ways out. The real fix is to move the account to caching_sha2_password with ALTER USER ... IDENTIFIED WITH caching_sha2_password BY '...', after checking that the application's driver understands it. The stopgap is to start the server with mysql_native_password=ON, which works on 8.4 and nowhere after it: MySQL 9.0 removed the plugin entirely. The errors that look similar but come from the other side - an old client that cannot speak caching_sha2_password, or a new 9.x client that can no longer speak mysql_native_password - have their own fixes, and telling them apart is most of the work.
This post covers the authentication side of the 8.4 upgrade in depth. The wider list of what changed between 8.0 and 8.4 is in MySQL 8.4 LTS: what changed, and creating accounts and granting privileges is in MySQL users and privileges.
What changed, version by version#
The plugin has been on its way out for years. What differs between versions is how loudly MySQL tells you.
| Version | mysql_native_password | Default for new accounts |
|---|---|---|
| 5.7 | Available, the default | mysql_native_password |
| 8.0 | Available, deprecated (warnings from 8.0.34) | caching_sha2_password |
| 8.4 LTS | Present, disabled unless enabled at start-up | caching_sha2_password |
| 9.0 and later | Removed from the server | caching_sha2_password |
The 8.0 years are why so many installations still have native accounts. The default changed in 8.0.4, but a server upgraded from 5.7 kept every existing account on its old plugin, and plenty of setup guides, Docker Compose files and hosting control panels set default_authentication_plugin=mysql_native_password to avoid driver trouble. Those accounts carried on working through the whole 8.0 series. 8.4 is where that stops.
Note the subtle point in the 8.4 row. The plugin is not gone: the code is still in the server, accounts that use it still exist in mysql.user, and the data survives the upgrade untouched. It is simply not loaded, so a login against such an account cannot be completed, and neither can a CREATE USER or ALTER USER that names it. On 9.x the same accounts still exist after an upgrade, but there is no switch left to turn them back on.
How the two plugins differ#
mysql_native_password stores a double SHA-1 hash of the password, unsalted. Two accounts with the same password have the same hash, and SHA-1 is fast enough that a leaked mysql.user table can be brute-forced cheaply. The login itself is a challenge-response that never sends the password, which is why it worked without TLS for two decades.
caching_sha2_password stores a salted SHA-256 hash put through thousands of rounds, which makes stored hashes far more expensive to attack. The cost is that checking a password against that hash is deliberately slow, so the server keeps an in-memory cache of accounts that have recently logged in successfully. That gives the plugin two paths:
- Fast authentication - the account is in the cache, so the client answers a scramble with a SHA-256 based response and is let in without the password ever crossing the wire. This is the normal case.
- Full authentication - the account is not in the cache, which happens on the first login after a server start, after
FLUSH PRIVILEGES, and after the account's password changes. The server needs the actual password to check it against the stored hash, so the client must send it, and MySQL will only accept it over a secure channel: a TLS connection, a Unix socket, or encrypted with the server's RSA public key.
That second path is the source of the "it works and then it does not" reports. An application connects without TLS, everything is fine for days because the account is cached, the server restarts, and the next login fails until something logs in once over TLS and warms the cache again. Configure the client for TLS or for RSA key retrieval from the start and the problem never appears.
The errors, and which side they come from#
Every authentication failure in this area comes from one of three situations: the server cannot run the account's plugin, the client cannot run the plugin the server asked for, or the client can run it but the connection is not secure enough for full authentication.
| Error | Comes from | Meaning |
|---|---|---|
ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded | Server | The account uses the native plugin and the server has it off or removed |
ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded | Client | The client library is too old for the new plugin |
ERROR 2059 (HY000): Authentication plugin 'mysql_native_password' cannot be loaded | Client | A 9.x client connecting to an account or server that still uses the old plugin |
ERROR 2061 (HY000): ... Authentication requires secure connection | Client | Full authentication over a plain connection with no RSA key |
Plugin 'mysql_native_password' is not loaded
This is the 8.4 and 9.x server telling you about the account, not the client. Any client, however new, gets it when logging in as an account whose plugin column still says mysql_native_password. You also get it as a statement error when running CREATE USER ... IDENTIFIED WITH mysql_native_password - a line that appears in a great many old install guides and provisioning scripts. Fix the account, or on 8.4 enable the plugin; on 9.x only the first is possible.
Authentication plugin 'caching_sha2_password' cannot be loaded
The full message usually ends with a path, something like /usr/lib/mysql/plugin/caching_sha2_password.so: cannot open shared object file. The client is linked against a library from the MySQL 5.7 era (or an old MariaDB client library) that has never heard of caching_sha2_password, so it goes looking for a plugin file on disk and finds nothing. Creating that file does not help. Upgrade the client or driver; changing the account back to the native plugin is the wrong direction, since 9.x cannot do it at all.
Authentication plugin 'mysql_native_password' cannot be loaded
This is the mirror image, and it has become common since MySQL 9.x clients arrived in package managers - Homebrew's mysql formula in particular. The 9.x client no longer ships the native plugin, so pointing it at an old 5.7 server, at a MariaDB server, or at an 8.x account still on the old plugin fails on the client side. The fixes are to move the account to caching_sha2_password if the server supports it, or to use an 8.4 LTS client for servers that do not (brew install mysql-client@8.4 on a Mac).
Driver-specific spellings
Drivers that implement the protocol themselves word the same failures differently:
| Driver | Message |
|---|---|
PHP mysqli / PDO, old PHP | The server requested authentication method unknown to the client [caching_sha2_password] |
Node.js mysql package | ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server |
| Java Connector/J | Public Key Retrieval is not allowed |
The Java one is not a version problem: Connector/J understands the plugin but refuses, by default, to fetch the RSA key over an unencrypted connection. It is the same full-authentication situation as ERROR 2061, covered below.
Find the accounts still on the old plugin#
Before changing anything, list them. Run this as root or another account that can read the mysql schema:
SELECT user, host, plugin, password_last_changedFROM mysql.userWHERE plugin = 'mysql_native_password'ORDER BY user, host;Expect to see application accounts created by old scripts, accounts migrated from a 5.7 server by dump and load (SHOW CREATE USER output carries the plugin and hash with it), and sometimes root itself. Internal accounts such as mysql.sys, mysql.session and mysql.infoschema use caching_sha2_password on a current server and are locked; leave them alone.
Remember that an account is a user and a host together, so 'app'@'%' and 'app'@'localhost' are separate rows and may use different plugins. An application that connects from two places may hit the problem from one of them only. SELECT CURRENT_USER(); after logging in shows which row a session actually matched.
The MySQL Shell upgrade checker reports the same thing as part of its wider scan, if you are checking an 8.0 server before an upgrade:
$ mysqlsh root@db.example.net:3306 -- util check-for-server-upgrade --target-version=8.4.6Check the client before you move the account#
Moving an account to caching_sha2_password before its client supports it swaps one outage for another. Anything released in the last several years is fine; the problem is old pinned dependencies and old system packages.
| Client or driver | Supports caching_sha2_password |
|---|---|
PHP mysqli and PDO (mysqlnd) | Yes on PHP 7.4 and later |
Node.js mysql2 | Yes |
Node.js mysql (mysqljs) | No - switch to mysql2 |
| Python PyMySQL, mysql-connector-python | Yes on current releases |
| Java Connector/J | 8.0 and later |
| .NET MySqlConnector, MySql.Data | Yes; MySql.Data from 8.0 |
Go go-sql-driver/mysql | 1.4 and later |
mysql CLI, mysqldump | 8.0 and later |
A few practical notes on the rows above.
PHP. Support was added during the 7.1 and 7.2 series, but PHP that old is long out of security support and should be upgraded anyway. On PHP 7.4 and every 8.x release, mysqli and pdo_mysql use mysqlnd and handle both plugins. A WordPress or Laravel site failing with the "unknown to the client" message is almost always running on an old PHP binary somewhere you did not expect - a cron container, a CLI on an old image - while the web server's PHP is fine. PHP PDO and MySQL covers the connection options.
Node.js. The original mysql package has never supported caching_sha2_password and is effectively unmaintained. mysql2 has a near-identical API for most code and supports both plugins plus promises; for most projects the change is the package name and an import line. ORMs such as Sequelize, Knex and TypeORM already use mysql2 for MySQL in their current versions.
Python. mysqlclient (the MySQLdb module) is a wrapper around the C client library it was built against, so its support depends on that library rather than on its own version. A wheel built against a MySQL 8 or later client library works; one built against an old library does not.
Desktop tools. MySQL Workbench 8.x, DBeaver and HeidiSQL all handle the new plugin in current versions; the trouble comes from copies installed years ago and never updated. MySQL GUI clients has the connection settings for each.
Migrating an account with ALTER USER#
The statement that moves an account is short, and it needs the password:
ALTER USER 'app'@'%' IDENTIFIED WITH caching_sha2_password BY 'the-current-or-a-new-password';The stored hashes of the two plugins are different functions of the password, and a SHA-1 hash cannot be converted into a SHA-256 one. MySQL has no way to switch the plugin and keep the old hash, so you supply a password - the same one the application already uses, if you know it and want no configuration change, or a new one. If nobody knows the current password, this is your chance to generate a proper one and put it where the application reads it.
This statement does not need the native plugin to be enabled. On a stock 8.4 server, or on 9.x after an upgrade, root can migrate a native account directly; the account cannot log in until then, but it can be altered.
To migrate many accounts, generate the statements and fill in the passwords by hand or from your secrets store:
SELECT CONCAT('ALTER USER ', QUOTE(user), '@', QUOTE(host), ' IDENTIFIED WITH caching_sha2_password BY ''CHANGE_ME'';') AS stmtFROM mysql.userWHERE plugin = 'mysql_native_password';After each one, confirm the result and test a login as that account from where the application runs:
SHOW CREATE USER 'app'@'%';$ mysql -h db.example.net -P 3306 -u app -p --ssl-mode=REQUIRED -e "SELECT CURRENT_USER();"Moving without downtime
Changing the plugin and the password of a live account is a moment where running instances with the old credentials stop being able to open new connections. Existing connections stay up, since authentication only happens at connect time, but a pool that recycles a connection will fail. For a single small app with a restart a few seconds later, that is acceptable. For anything busier, there are two clean routes.
- A second account. Create
'app2'@'%'withcaching_sha2_passwordand the same grants (SHOW GRANTS FOR 'app'@'%'gives you the statements to copy), switch the application's configuration to it, deploy, confirm nothing is still connecting as the old account, thenDROP USER 'app'@'%'. - Same account, same password, quick deploy. If you keep the password unchanged, the only thing that changes is the plugin. Clients that already support
caching_sha2_passwordreconnect without any configuration change, so the migration is invisible as long as the driver was upgraded first.
The dual-password feature (RETAIN CURRENT PASSWORD) does not help here: MySQL does not allow it in an ALTER USER that changes the authentication plugin.
TLS, RSA keys and the first-login failure#
Once accounts are on caching_sha2_password, the remaining failures are about full authentication needing a secure channel. The clean answer is TLS: MySQL 8.x generates a self-signed certificate on first start, and the client libraries try TLS by default (--ssl-mode=PREFERRED). A client that has TLS disabled, or a library that does not do TLS by default, falls back to RSA, and some refuse to fetch the key unless told to.
| Client | Setting that allows full auth without TLS |
|---|---|
mysql CLI | --get-server-public-key |
| Connector/J | allowPublicKeyRetrieval=true |
| MySqlConnector (.NET) | AllowPublicKeyRetrieval=true |
Retrieving the public key over an unencrypted connection is open to a man-in-the-middle substituting their own key, which is why it is off by default. It is a reasonable compromise on a private network; across the internet, use TLS instead and leave key retrieval off. A JDBC URL that does it properly looks like this:
jdbc:mysql://db.example.net:3306/appdb?sslMode=REQUIREDThe Unix socket counts as secure, so local connections through localhost on the same machine never hit this. Remote connections are where it matters, and MySQL remote connections covers the network side - firewalls, bind addresses and certificate checking.
Turning mysql_native_password back on in 8.4#
When an application cannot be fixed today - a vendor binary with an old client library inside it, say - 8.4 lets you load the plugin again. It is a server start-up option:
[mysqld]mysql_native_password=ONThe command-line form is --mysql-native-password=ON, which is how it goes in a Docker Compose command: line. Restart the server and check that it took:
SELECT plugin_name, plugin_statusFROM information_schema.pluginsWHERE plugin_name = 'mysql_native_password';An ACTIVE status means accounts on the old plugin can log in again. A row with another status, or no row at all, means the option was not read - check which configuration files the server actually loads with mysqld --verbose --help | grep -A1 "Default options".
Two traps catch people here. The first is the 8.0-era setting default_authentication_plugin=mysql_native_password, which many old Compose files and my.cnf snippets carry. That variable was removed in 8.4, and the server refuses to start with it in the configuration, reporting an unknown variable in the error log. Delete it; if you really need native as the default for new accounts, the 8.4 equivalent is authentication_policy, but there is little reason to create new native accounts on a server where the plugin has no future. The second trap is expecting this option to work on 9.x. It does not exist there, and an unknown option prevents start-up just the same.
On a hosted database the server's configuration belongs to the provider, so whether you can set this at all depends on them. Plan on migrating the accounts instead; it takes minutes and does not need anyone's permission.
Upgrading from 8.4 to 9.x#
MySQL 9.x Innovation releases and the next LTS have no native plugin at all. An upgrade with native accounts still present succeeds - the server starts and the data is intact - but those accounts are unusable until someone alters them, and the error is the same Plugin 'mysql_native_password' is not loaded. If root is among them, that someone has to get in another way, which is the restart-with---skip-grant-tables scenario you want to avoid.
So the order is fixed: upgrade drivers, migrate accounts on 8.4, confirm the query in the previous section returns no rows, and only then think about 9.x. For a production database there is little reason to run Innovation releases at all; 8.4 is supported for years yet. The point is that the migration is the same work whether you do it now or under pressure later.
When moving a database between servers with a logical dump, the same applies to the accounts you carry over. A SHOW CREATE USER statement copied from an old server includes IDENTIFIED WITH 'mysql_native_password' AS '*...', and replaying it on 8.4 fails or recreates the problem. Recreate the accounts with caching_sha2_password on the new server instead - migrating MySQL to a new host walks through that move. RE:NODE's MySQL plans run 8.4 LTS, and the root account, application database and application user are generated on the current default plugin, so the accounts you start with need no migration; the ones you bring from an older server might.
FAQ#
Is caching_sha2_password slower than mysql_native_password?
Only on full authentication, and only noticeably for workloads that open a new connection for every request with an empty cache. After the first login the account is cached and fast authentication costs about the same as the old plugin. A connection pool, which most applications use anyway, makes the difference disappear; connection limits and pooling explains how to size one.
Can I switch a user's plugin without knowing the password?
No. The plugin and the hash go together, and MySQL cannot derive a SHA-256 hash from a stored SHA-1 one. You need to set a password in the same ALTER USER statement. If the old password is lost, set a new one and update the application's configuration.
Why does the login work from my laptop but not from the server?
Usually because the two machines run different client libraries. The laptop has a current mysql CLI or GUI tool; the server's application uses an old driver baked into an image or a lock file. Compare the exact error text with the table above - it tells you whether the server or the client is refusing.
Does MariaDB have the same change?
No, and they should not be treated as interchangeable here. MariaDB still uses mysql_native_password and its own plugins, and does not implement MySQL's caching_sha2_password on the server. That is also why a MySQL 9.x client cannot log in to many MariaDB servers: it has dropped the only plugin they share.
Do I need FLUSH PRIVILEGES after ALTER USER?
No. ALTER USER updates the in-memory grant and authentication caches immediately. Running FLUSH PRIVILEGES anyway also clears the caching_sha2_password cache, which forces every account through full authentication on its next login - harmless with TLS, and a source of surprise failures for clients without it.


