Skip to main content

How to Resolve Python Error "ssl.SSLError: [SSL: WRONG_VERSION_NUMBER]"

The ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number is a common Python error encountered when establishing a secure connection (SSL/TLS). It fundamentally indicates a mismatch in the expected communication protocol during the initial handshake phase, most often because the client and server disagree on whether SSL/TLS should be used immediately on the connected port, or if plain communication should start first.

This guide explains the common causes of this error, particularly in contexts like smtplib (email), requests/urllib3 (HTTP), and Django email configurations, and provides clear solutions.

Understanding the Error: SSL/TLS Handshake and Protocol Mismatch

Secure connections (like HTTPS or SMTPS) use Transport Layer Security (TLS) or its predecessor, Secure Sockets Layer (SSL), to encrypt data. Establishing this connection involves a "handshake" where the client and server negotiate the protocol version and encryption methods.

The WRONG_VERSION_NUMBER error typically occurs during this initial handshake. It means one side (e.g., your Python client) started the handshake expecting a certain SSL/TLS protocol negotiation, but the other side (the server) responded with data that doesn't conform to that expected protocol version for that specific port. This usually happens because:

  • The client connected to a port expecting immediate SSL/TLS (like port 465 for SMTPS) but tried to speak plain text first.
  • The client connected to a port expecting plain text first, followed by an upgrade to TLS (like port 587 for SMTP with STARTTLS), but tried to initiate an SSL handshake immediately.
  • Less commonly, actual SSL/TLS version incompatibilities or proxy interference.

Common Cause 1 (smtplib): Incorrect Port for SSL vs. TLS

This is a frequent cause when sending emails using Python's smtplib. Email servers use different ports for different connection methods:

  • Port 465 (SMTPS): Expects an immediate SSL connection upon connecting. Use with smtplib.SMTP_SSL.
  • Port 587 (SMTP with STARTTLS): Expects an initial plain text connection, which is then upgraded to secure TLS using the STARTTLS command. Use with smtplib.SMTP followed by server.starttls().

The error occurs if you mismatch the smtplib class/method and the port number.

Error Scenario (SMTP_SSL with Non-SSL Port)

Using SMTP_SSL (which initiates SSL immediately) but connecting to port 587 (which expects plain text first) is a common mistake.

# Error Scenario
import smtplib

port = 587 # Incorrect port for SMTP_SSL
smtp_server = 'smtp.gmail.com'
sender_email = '[email protected]'
password = 'your_password'
receiver_email = "[email protected]"
message = "Subject: Test\n\nThis is a test."

try:
# Using SMTP_SSL which expects immediate SSL on port 465
# But connecting to port 587 which expects plain text first
# ⛔️ ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number
with smtplib.SMTP_SSL(smtp_server, port) as server:
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
except smtplib.SMTPConnectError as e:
print(f"Connect Error: {e}")
except smtplib.SMTPServerDisconnected as e:
print(f"Server Disconnected: {e}")
except smtplib.SMTPAuthenticationError as e:
print(f"Auth Error: {e}")
except ConnectionRefusedError as e:
print(f"Connection Refused: {e}")
except Exception as e: # Catching generic Exception to show the SSL error if others don't match
print(f"Caught Error: {e}") # Will likely print the SSLError here

Solution: Match Port and Class/Method (465 for SMTP_SSL, 587 for starttls)

Ensure the port number matches the connection method:

  • If using smtplib.SMTP_SSL: Use port 465.
    import smtplib
    port = 465 # ✅ Correct port for SMTP_SSL
    # ... other variables ...
    try:
    with smtplib.SMTP_SSL(smtp_server, port) as server:
    # ... login and send ...
    print("Email sent successfully using SMTP_SSL on port 465.")
    except Exception as e: print(f"Error: {e}")
  • If using smtplib.SMTP and upgrading with server.starttls(): Use port 587. (See Recommended Approach below).

Using explicit TLS via the STARTTLS command on port 587 is generally preferred over implicit SSL on port 465 (which is somewhat deprecated).

# ✅ Recommended: TLS on port 587
import smtplib
import ssl

port = 587 # ✅ Correct port for STARTTLS
smtp_server = 'smtp.gmail.com'
sender_email = '[email protected]'
password = 'your_password'
receiver_email = "[email protected]"
message = "Subject: Test TLS\n\nThis is a test using TLS."

# Create a default SSL context for security
context = ssl.create_default_context()

try:
# Connect using standard SMTP class
with smtplib.SMTP(smtp_server, port) as server:
# Upgrade the connection to secure TLS
server.starttls(context=context) # ✅ Upgrade step
# Login and send over the secure connection
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
print("Email sent successfully using STARTTLS on port 587.")
except Exception as e:
print(f"Error: {e}")

Common Cause 2 (requests/urllib3): Proxy or Library Issues

While less common, you might see this error when making HTTPS requests, potentially due to issues with underlying libraries like urllib3 or proxy configurations.

Solution: Upgrade urllib3

Older versions of urllib3 (a dependency of requests) sometimes had bugs related to SSL/TLS handling. Upgrading it might resolve the issue.

pip install --upgrade urllib3
# Or use
pip3 / python -m pip

After upgrading, retry your requests code.

Solution: Check HTTPS Proxy Configuration

If you are using a proxy for HTTPS traffic, there's a known interaction/bug in some older setups where the https proxy URL needs to be specified using the http:// protocol prefix in the proxies dictionary for requests. This tells requests to connect to the proxy using plain HTTP first, and then the proxy handles the subsequent TLS connection to the destination. This is counter-intuitive.

import requests

https_proxy_url = "http://your-proxy-address:proxy-port" # Note: uses http://

proxies = {
"http": "http://your-proxy-address:proxy-port", # Optional: http proxy
"https": https_proxy_url # ✅ Specify HTTPS proxy using http:// URL
}

target_url = "https://example.com" # The secure site you want to reach

try:
response = requests.get(
target_url,
proxies=proxies,
verify=True # Keep SSL verification if possible
)
response.raise_for_status()
print(f"Successfully connected to {target_url} via proxy.")
except requests.exceptions.RequestException as e:
print(f"Request failed via proxy: {e}") # Error might occur here if proxy setup is wrong

Consult the requests documentation on proxies for more details. Using an https:// URL for the https proxy key often leads to the WRONG_VERSION_NUMBER error because requests tries to establish TLS with the proxy itself immediately, which the proxy might not expect.

Common Cause 3 (Django): Incorrect Email Settings

In Django projects using SMTP for email, incorrect configuration in settings.py can cause this error when sending emails.

Solution: Match EMAIL_PORT to EMAIL_USE_TLS/EMAIL_USE_SSL

Django needs to know which security protocol and port to use. Ensure your settings are consistent:

  • If using TLS (Recommended):
    # settings.py
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_HOST = 'smtp.example.com' # Your SMTP host
    EMAIL_USE_TLS = True # ✅ Use TLS
    EMAIL_USE_SSL = False # ✅ Mutually exclusive with TLS
    EMAIL_PORT = 587 # ✅ Port for TLS
    EMAIL_HOST_USER = '[email protected]'
    EMAIL_HOST_PASSWORD = 'your_password'
  • If using SSL:
    # settings.py
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_HOST = 'smtp.example.com'
    EMAIL_USE_SSL = True # ✅ Use SSL
    EMAIL_USE_TLS = False # ✅ Mutually exclusive with SSL
    EMAIL_PORT = 465 # ✅ Port for SSL
    EMAIL_HOST_USER = '[email protected]'
    EMAIL_HOST_PASSWORD = 'your_password'

Crucially:

  • Set either EMAIL_USE_TLS or EMAIL_USE_SSL to True, not both.
  • Set EMAIL_PORT to 587 if EMAIL_USE_TLS is True.
  • Set EMAIL_PORT to 465 if EMAIL_USE_SSL is True.

Mismatching these settings (e.g., EMAIL_USE_SSL = True with EMAIL_PORT = 587) will likely cause the WRONG_VERSION_NUMBER error.

Conclusion

The ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] typically indicates a protocol mismatch during the SSL/TLS handshake, often related to incorrect port usage.

Key solutions include:

  1. For smtplib: Ensure you use port 465 with smtplib.SMTP_SSL or (preferably) port 587 with smtplib.SMTP followed by server.starttls().
  2. For requests/urllib3: Try upgrading urllib3. If using proxies for HTTPS, ensure the proxy URL specified for the https key in the proxies dict uses an http:// prefix.
  3. For Django: Correctly configure settings.py, ensuring EMAIL_PORT (587 or 465) matches the EMAIL_USE_TLS (True/False) or EMAIL_USE_SSL (True/False) setting, with only one of the latter being True.

By aligning the expected protocol (implicit SSL vs. explicit STARTTLS) with the correct port number and ensuring library compatibility, you can effectively resolve this SSL error.