Hi! 👋 We are doing a big documentation refresh. Help us improve — what's missing or could be better? Let us know! Simply send an email or start a conversation in Google Groups!

MySQL Pass-Through Authentication

Pass-through authentication lets ProxySQL learn a MySQL credential by validating it against a backend. It is useful when ProxySQL should route a user without storing that user’s password in mysql_users, but it expands the authentication and in-memory credential surface and must be enabled deliberately.

Availability

Pass-through authentication can be enabled in ProxySQL 3.1.10 and 4.0.10. ProxySQL 3.0.10 exposes the variables but is the Stable tier: loading mysql-passthrough_auth_enabled=true logs an error and forces the runtime value back to false.

The master gate mysql-passthrough_auth_enabled defaults to false. All subordinate pass-through settings are ineffective while it remains off.

Security Model

For a cache miss, ProxySQL requests the client’s cleartext credential during the caching_sha2_password full-authentication exchange and uses a fresh backend connection as the credential verdict. The frontend connection must use TLS. Keep mysql-passthrough_auth_require_tls at its default true, and configure use_ssl=1 on target backend rows so the validation connection is also protected.

A successful credential is retained as cleartext in ProxySQL process memory. The read-only statistics never expose it, but process-memory access remains sensitive. Learned credentials:

  • are not written to mysql_users or any other Admin table;
  • are not persisted to disk and disappear on restart; and
  • are local to one ProxySQL process and are not synchronized by ProxySQL Cluster.

Use least-privilege Admin access, restrict the frontend listener, protect core dumps and process memory, and set a finite cache TTL when operationally appropriate.

Supported Authentication Flow

The released learning flow supports caching_sha2_password full authentication. On a cache miss ProxySQL:

  1. checks the master gate, user-mode gate, TLS, username policy, failure limits, and in-flight limit;
  2. receives the credential over the TLS-protected frontend connection;
  3. forces a new connection to the selected backend hostgroup so the submitted credential is actually tested;
  4. caches the credential and returns the authenticated backend connection to the normal pool on success; and
  5. completes frontend authentication. The client’s first query then follows normal lazy backend acquisition.

The probe counts against both the target mysql_servers.max_connections and the pass-through in-flight cap. Failures use a generic access-denied response so backend topology and error detail are not disclosed.

Empty-Password Rows

An existing mysql_users row with password='' enters pass-through only when the master gate and mysql-passthrough_auth_empty_password are both true. Its default_hostgroup selects the validation backend and its normal user settings remain the routing policy.

Enabling this mode changes the meaning of the empty password: it becomes a pass-through signal, not permission for passwordless login. Clients must send a valid backend password. Do not enable the mode if an existing deployment intentionally relies on passwordless empty-password rows.

Unknown Users

mysql-passthrough_auth_unknown_users defaults to false. When enabled, a username absent from mysql_users can be validated against mysql-passthrough_default_hg. ProxySQL still does not create a mysql_users row.

Routing defaults are synthesized again on every connect. The default schema comes from mysql-passthrough_default_schema, falling back to mysql-default_schema when empty. Restrict the allowed population with mysql-passthrough_auth_username_pattern. The pattern is a full-match allowlist for every pass-through attempt, including empty-password rows, and is especially important for unknown users. An empty pattern permits every username and emits a security warning when unknown-user mode is loaded.

Configuration

This example enables the safer, explicit empty-password-row flow. Replace the username, password handling, hostgroup, and listener TLS configuration for your environment:

UPDATE mysql_servers
SET use_ssl = 1
WHERE hostgroup_id = 30;

SET mysql-default_authentication_plugin = 'caching_sha2_password';
SET mysql-passthrough_auth_enabled = 'true';
SET mysql-passthrough_auth_empty_password = 'true';
SET mysql-passthrough_auth_unknown_users = 'false';
SET mysql-passthrough_auth_require_tls = 'true';
SET mysql-passthrough_auth_cache_ttl_s = 3600;

LOAD MYSQL SERVERS TO RUNTIME;
LOAD MYSQL VARIABLES TO RUNTIME;

SELECT variable_value
FROM runtime_global_variables
WHERE variable_name = 'mysql-passthrough_auth_enabled';

The query must return true. If it remains false, stop here: do not create, load, or save an empty-password user row. In particular, ProxySQL 3.0.10 Stable rejects the enabled value during the runtime load and forces it back to false. Also inspect the LOAD MYSQL VARIABLES TO RUNTIME result for rejected or unknown settings.

Only after the runtime master gate is confirmed as true, persist the verified variables and backend settings, then create and persist the pass-through user. This order prevents the empty-password row from existing on disk without its required gate:

SAVE MYSQL VARIABLES TO DISK;
SAVE MYSQL SERVERS TO DISK;

INSERT INTO mysql_users (username, password, default_hostgroup, active)
VALUES ('app_user', '', 30, 1);

LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS TO DISK;

For an unknown-user flow, do not insert the user row and add these settings before loading variables:

SET mysql-passthrough_auth_unknown_users = 'true';
SET mysql-passthrough_auth_empty_password = 'false';
SET mysql-passthrough_default_hg = 30;
SET mysql-passthrough_default_schema = 'app';
SET mysql-passthrough_auth_username_pattern = '^app_[a-z0-9_]+$';

Variable defaults, accepted ranges, and tier availability are in the canonical MySQL variables reference. User and backend fields are defined in mysql_users and mysql_servers.

Credential Cache and Expiration

mysql-passthrough_auth_cache_ttl_s defaults to 0, meaning no time-based expiration. A positive TTL is checked lazily on lookup; expired entries are removed and the next connection must validate again.

If a later query-path backend connection rejects a learned credential with access denied, ProxySQL evicts that user immediately. The current client connection fails, and the next connection revalidates. Operators can also flush the whole cache or one user:

PROXYSQL FLUSH PASSTHROUGH_AUTH_CACHE;
PROXYSQL FLUSH PASSTHROUGH_AUTH_CACHE FOR USER 'app_user';

Disabling the master gate prevents new pass-through attempts but does not erase existing entries. Flush them explicitly when the operational intent is to remove learned credentials.

Failure Limits

Pass-through uses a per-username and per-client-IP sliding failure window plus a global in-flight probe cap. Credential-class backend failures count toward the sliding limits; transport failures and timeouts do not. Further attempts are rejected until the applicable window clears.

The controls are:

Keep the defaults until observed traffic justifies a change. Raising the limits expands the credential-stuffing and backend-load budget.

Verification and Statistics

Verify runtime settings, cache state, and health counters from the Admin interface:

SELECT variable_name, variable_value
FROM runtime_global_variables
WHERE variable_name LIKE 'mysql-passthrough%'
ORDER BY variable_name;

SELECT username, learned_at, age_s, hostgroup_probed
FROM stats_mysql_passthrough_auth_cache;

SELECT metric_name, metric_value
FROM stats_mysql_passthrough_auth_metrics;

The cache table contains usernames and timing/hostgroup metadata, never passwords or hashes. Treat it as sensitive when unknown users are enabled because it reveals accounts that successfully authenticated. Watch probes_failed_credentials, probes_failed_transport, lockout counters, inflight_cap_rejects, and cache_invalidations alongside successful probes and hits. See the canonical pass-through statistics reference.

Unsupported Cases

  • mysql_native_password and mysql_clear_password cannot initiate released pass-through learning.
  • COM_CHANGE_USER to a user that requires pass-through is rejected.
  • A non-TLS frontend connection cannot complete the supported learning flow.
  • ProxySQL 3.0.10 Stable cannot enable the master gate.
  • Learned credentials do not persist or replicate between ProxySQL nodes.

These exclusions apply to the released 3.1.10 and 4.0.10 behavior; do not plan deployments around unreleased authentication paths.

Troubleshooting

  • Master gate remains false: confirm the running tier. Stable 3.0.10 deliberately rejects enabling it; install 3.1.10 or 4.0.10.
  • Client receives access denied before a backend probe: confirm frontend TLS, the empty-row or unknown-user gate, and the username pattern. Invalid patterns deny access.
  • Probe starts but authentication fails: verify the backend account, password, target hostgroup, online server capacity, backend TLS, and caching_sha2_password support.
  • A valid user becomes temporarily locked out: inspect the per-user/IP counters and failure window. Fix the credential failures instead of immediately raising limits.
  • Password rotation causes one failed connection: query cache_invalidations; the rejected learned entry is evicted and the next connection should revalidate. Flush the user explicitly to preempt that failure.
  • Different nodes behave differently: the credential cache is node-local. Warm each node through normal authentication or expect the first connection on each node to validate independently.