How to fix ERROR 2026: SSL connection error in MySQL

MySQLINTERMEDIATEHIGHUpdated Jun 10, 2026· 1,557 views

MySQL ERROR 2026 (HY000) is a generic SSL/TLS handshake failure that happens before authentication. In modern setups the most common cause is a TLS version mismatch (old clients negotiating TLSv1.0/1.1, which MySQL removed in 8.0.28), followed by untrusted or self-signed CA certificates. Diagnose it with a single openssl s_client probe, then either trust the CA, skip certificate verification while keeping encryption, or upgrade the client's TLS support.

Quick fix

Fixes ERROR 2026: SSL connection error

openssl s_client -connect hostname:3306 -starttls mysql -CAfile ca.pem
  1. Probe the handshake with openssl s_client first
  2. Check for a TLS version mismatch and fix the client side
  3. Trust the server's CA (managed databases: RDS, Azure)
  4. Verify SSL is actually enabled and configured on the server
mysql — error
openssl s_client -connect hostname:3306 -starttls mysql -CAfile ca.pem
ERROR 2026: SSL connection error

What this error means

ERROR 2026 (HY000) is a generic SSL/TLS connection error raised when the MySQL client cannot complete a secure handshake with the server. It occurs during the TLS handshake phase, before authentication is attempted, so it almost always points at the transport layer rather than credentials. Because it is a catch-all, the message alone rarely identifies the root cause: it can mean the negotiated TLS protocol versions do not overlap, the server certificate is self-signed or signed by an untrusted CA, a certificate is expired or has a hostname mismatch, a certificate file is missing or unreadable, or the client is speaking plaintext to a TLS endpoint (or vice versa). The single most useful next step is to reproduce the handshake outside MySQL with openssl s_client, which surfaces the exact OpenSSL error behind the generic 2026.

Typical symptoms

  • Connection fails immediately during the TLS handshake, before any authentication or query
  • Client receives "SSL connection error" with little or no additional detail
  • Some clients connect successfully while others fail with the SSL error (usually a client TLS-version or CA-trust difference)
  • Error appears after upgrading the MySQL server to 8.0.28 or later, where TLSv1.0/1.1 were removed
  • Connections succeed without TLS but fail with --ssl-mode=REQUIRED or --ssl-mode=VERIFY_CA
  • Connections to managed databases (AWS RDS, Azure MySQL) fail with certificate / unknown-CA errors
  • openssl s_client reports "unsupported protocol" or "SSL_ERROR_RX_RECORD_TOO_LONG"

Common causes

  1. TLS protocol version mismatch: the client offers only TLSv1.0/1.1, which were deprecated in MySQL 8.0.26 and removed in 8.0.28, so the server (TLSv1.2/1.3 only) rejects the handshake
  2. Server certificate is self-signed or signed by an internal/private CA the client does not trust, so verification fails even though encryption is available
  3. Client lacks the correct CA bundle for a managed database (e.g. AWS RDS global-bundle.pem, Azure DigiCert root)
  4. Server certificate is expired, not yet valid, or its CN/SAN does not match the connection hostname
  5. A certificate file (ca/cert/key) is missing, has the wrong path, or is not readable by the connecting user or container
  6. Client library is outdated and cannot negotiate the TLS version the server requires
  7. Client is sending plaintext to a TLS port or TLS to a plaintext port (surfaces as SSL_ERROR_RX_RECORD_TOO_LONG)
  8. System clocks on client and server are far out of sync, causing certificate validity-window checks to fail

How to fix "ERROR 2026: SSL connection error"

1Probe the handshake with openssl s_client first

Before changing any config, reproduce the failure outside MySQL. This is the single best diagnostic because it turns the generic ERROR 2026 into the exact OpenSSL error.

bash
openssl s_client -connect hostname:3306 -starttls mysql -CAfile ca.pem

Interpret the output:
- Verify return code: 0 (ok) - certificate chain is valid; the problem is elsewhere (client TLS version or client config).
- Verify return code: 20 (unable to get local issuer certificate) - the client does not have the server's CA; supply the right CA bundle (see the RDS/Azure step).
- Verify return code: 21 (unable to verify the first certificate) / self-signed - untrusted or self-signed CA.
- error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol - TLS version mismatch (see the next step).
- SSL_ERROR_RX_RECORD_TOO_LONG - you are speaking TLS to a plaintext endpoint (or hitting a non-MySQL service); confirm the host/port and that the server actually has TLS enabled.

Pin a version to confirm a mismatch:

bash
openssl s_client -connect hostname:3306 -starttls mysql -tls1_2 -CAfile ca.pem

If -tls1_2 succeeds but the default fails, your client was trying an unsupported (old) protocol.

2Check for a TLS version mismatch and fix the client side

TLSv1.0 and TLSv1.1 were deprecated in MySQL 8.0.26 and removed in 8.0.28. A server on 8.0.28+ only accepts TLSv1.2/1.3, so any client that can only offer 1.0/1.1 fails with ERROR 2026 and the unsupported protocol signature.

Check what the server allows:

sql
SHOW VARIABLES LIKE 'tls_version';

The correct fix is almost always to give the client modern TLS support rather than weakening the server:
- mysql CLI: update the MySQL client package to a current 8.0+ build
- Python: upgrade mysql-connector-python or PyMySQL (and ensure a recent OpenSSL)
- Node.js: npm install mysql2@latest
- PHP: PHP 7.4+ linked against OpenSSL 1.1.1+

Do NOT downgrade or stay on MySQL 8.0.27 or earlier just to keep TLSv1.0/1.1 alive. Those protocols are obsolete and insecure (POODLE, BEAST, weak ciphers); re-enabling them re-exposes you to known attacks. If you genuinely cannot upgrade a legacy client in time, treat it as a temporary, network-isolated exception and plan the upgrade — do not make it the permanent posture.

3Trust the server's CA (managed databases: RDS, Azure)

If openssl reported an untrusted issuer (return code 20/21), the encryption is fine — the client just needs the right CA bundle. This is the normal situation for managed databases.

AWS RDS — download the global bundle and point --ssl-ca at it:

bash
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
mysql -h your-instance.region.rds.amazonaws.com -u admin -p \
  --ssl-ca=global-bundle.pem --ssl-mode=VERIFY_CA

Azure MySQL — use Microsoft's root certificate:

bash
mysql -h your-instance.mysql.database.azure.com -u username -p \
  --ssl-ca=DigiCertGlobalRootCA.crt --ssl-mode=REQUIRED

For a private/internal CA, hand the client the CA file the server's cert was signed by:

bash
mysql -h hostname -u user -p --ssl-ca=/path/to/internal-ca.pem --ssl-mode=VERIFY_CA

Providing the CA keeps both encryption AND verification on, which is the goal.

4Verify SSL is actually enabled and configured on the server

If openssl could not establish TLS at all, confirm the server supports and is configured for it. Connect locally on the server (or with a working client):

sql
SHOW VARIABLES LIKE 'have_%ssl%';
SHOW VARIABLES LIKE 'ssl_%';

Expected:

bash
have_openssl: YES
have_ssl:     YES
ssl_ca:   /path/to/ca.pem
ssl_cert: /path/to/server-cert.pem
ssl_key:  /path/to/server-key.pem

If have_ssl/have_openssl is NO, the server binary lacks TLS support and must be reinstalled with OpenSSL. If the variables are empty, set them in the [mysqld] section of /etc/my.cnf (or /etc/mysql/my.cnf) and restart. Also confirm the certificate files exist and are readable by the mysql process / your client user:

bash
ls -la /path/to/ca.pem /path/to/server-cert.pem /path/to/server-key.pem
5Verify certificate hostname and clock sync

A valid CA but a hostname or validity-window mismatch still fails verification. Check the cert's CN/SAN:

bash
openssl x509 -in server-cert.pem -text -noout | grep -A1 "Subject Alternative Name"
openssl x509 -in server-cert.pem -noout -dates

- Connect using the exact name in the CN/SAN — if the cert says mysql.example.com, connecting to 127.0.0.1 or a raw IP will fail unless that IP is in the SAN.
- Confirm notBefore/notAfter cover the current time.

Large clock drift breaks the validity-window check, so keep both hosts on NTP:

bash
date          # compare client and server
timedatectl   # confirm NTP sync is active
6Decide encryption posture: skip verification vs disable TLS (they differ)

These are NOT the same thing, and the difference matters for ERROR 2026. The typical 2026 case (self-signed or internal CA on a server you control) should be solved by keeping encryption and skipping only certificate verification, not by turning TLS off.

Encrypt, but skip CA verification (good for self-signed/internal CA when you can't add the CA yet):

bash
# mysql CLI: encrypted, no CA/identity check
mysql --ssl-mode=REQUIRED -h hostname -u user -p
python
# Python mysql.connector: encrypted, skip cert verification
import mysql.connector
conn = mysql.connector.connect(
    host='hostname', user='user', password='password',
    ssl_disabled=False,           # keep TLS ON
    ssl_verify_cert=False,        # do not verify the server cert
    ssl_verify_identity=False,
)
javascript
// Node.js mysql2: encrypted, skip cert verification.
// NOTE: ssl: 'skip' is NOT a real mysql2 option and does nothing.
const connection = mysql.createConnection({
  host: 'hostname', user: 'user', password: 'password',
  ssl: { rejectUnauthorized: false }, // TLS stays ON, cert just isn't verified
});

Disable TLS entirely (no encryption at all — only on a trusted/loopback network, never the default):

bash
mysql --ssl-mode=DISABLED -h hostname -u user -p
python
conn = mysql.connector.connect(host='hostname', user='user', password='password', ssl_disabled=True)
javascript
// Omit the ssl key entirely to not negotiate TLS
const connection = mysql.createConnection({ host: 'hostname', user: 'user', password: 'password' });

Key distinction: --ssl-mode=REQUIRED / rejectUnauthorized:false / ssl_verify_cert:False keep traffic encrypted but skip identity checks (vulnerable to MITM but not plaintext). --ssl-mode=DISABLED / ssl_disabled=True / omitting ssl send credentials and data unencrypted. Default to encryption; reach for DISABLED only as a deliberate, isolated last resort.

Advanced notes

Version history matters when diagnosing ERROR 2026: TLSv1.0 and TLSv1.1 were deprecated in MySQL 8.0.26 and removed in 8.0.28, so a server upgrade across that boundary is a frequent trigger for previously-working legacy clients. The right response is to upgrade the client's TLS stack, not to pin MySQL to 8.0.27 or re-enable old protocols — TLS 1.0/1.1 carry known vulnerabilities and should only ever be tolerated as a short-lived, network-isolated exception with an explicit migration plan.

Note that --ssl-mode=REQUIRED enforces encryption but does NOT verify the CA or server identity; use VERIFY_CA to validate the chain and VERIFY_IDENTITY to also enforce hostname matching. For managed databases always use the provider's current CA bundle (AWS RDS global-bundle.pem, Azure DigiCert root, Google Cloud SQL server-ca.pem) since these rotate periodically.

Some client libraries ship their own TLS/CA store separate from the system OpenSSL — PHP's mysqlnd, for instance, may bundle its own certificates, so its effective OpenSSL behavior can differ from the system. In Docker, make sure the image includes OpenSSL and that mounted certificate files are readable by the in-container user.

Finally, ERROR 2026 accompanied by SSL_ERROR_RX_RECORD_TOO_LONG almost always means plaintext is being sent to a TLS endpoint (or you are hitting a non-TLS service). Note that 3306 is MySQL's normal protocol port, not a dedicated SSL port — MySQL 8 negotiates TLS opportunistically over that same connection rather than using a separate SSL port. So this signature usually points to a wrong host/port or a server without TLS enabled, not to a missing 'SSL port'.

Frequently asked questions

What does "ERROR 2026: SSL connection error" mean?
ERROR 2026 (HY000) is a generic SSL/TLS connection error raised when the MySQL client cannot complete a secure handshake with the server. It occurs during the TLS handshake phase, before authentication is attempted, so it almost always points at the transport layer rather than credentials. Because it is a catch-all, the message alone rarely identifies the root cause: it can mean the negotiated TLS protocol versions do not overlap, the server certificate is self-signed or signed by an untrusted CA, a certificate is expired or has a hostname mismatch, a certificate file is missing or unreadable, or the client is speaking plaintext to a TLS endpoint (or vice versa).
How do I fix "ERROR 2026: SSL connection error"?
MySQL ERROR 2026 (HY000) is a generic SSL/TLS handshake failure that happens before authentication. In modern setups the most common cause is a TLS version mismatch (old clients negotiating TLSv1.0/1.1, which MySQL removed in 8.0.28), followed by untrusted or self-signed CA certificates. Diagnose it with a single openssl s_client probe, then either trust the CA, skip certificate verification while keeping encryption, or upgrade the client's TLS support.
What causes "ERROR 2026: SSL connection error"?
TLS protocol version mismatch: the client offers only TLSv1.0/1.1, which were deprecated in MySQL 8.0.26 and removed in 8.0.28, so the server (TLSv1.2/1.3 only) rejects the handshake Server certificate is self-signed or signed by an internal/private CA the client does not trust, so verification fails even though encryption is available Client lacks the correct CA bundle for a managed database (e.g. AWS RDS global-bundle.pem, Azure DigiCert root)
Is the "ERROR 2026: SSL connection error" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading