Giải mã lỗi
Ít có điều gì làm đình trệ một dự án Python nhanh hơn một lỗi SSL khó hiểu. Có lẽ bạn đang thấy thông báo này trong terminal của mình ngay bây giờ:
ssl.SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:1129)
Đây là một lỗi dễ gây hiểu lầm. Mặc dù nó gợi ý rằng phiên bản TLS hoặc OpenSSL của bạn đã lỗi thời, nhưng thực tế hiếm khi như vậy. Trong 99% trường hợp, đây là sự không khớp về giao thức (protocol mismatch). Bạn đang cố gắng giao tiếp bằng "mã hóa" với một cổng (port) vốn chỉ hiểu "văn bản thuần túy" (plain text).
Nguyên nhân gốc rễ: Rào cản ngôn ngữ
Lỗi này xảy ra khi một client (như một script Python sử dụng requests) bắt đầu một quá trình bắt tay (handshake) TLS bảo mật với một cổng máy chủ được cấu hình cho HTTP tiêu chuẩn.
Hãy coi đó là một sự gián đoạn trong giao tiếp:
- Client: Gửi một gói tin "Client Hello"—một chuỗi nhị phân nhằm bắt đầu một phiên mã hóa.
- Server: Nó không được thiết lập để mã hóa. Nó thấy dữ liệu nhị phân, không nhận diện được và phản hồi bằng một chuỗi văn bản thuần túy như
HTTP/1.1 400 Bad Request. - Sự không khớp: Client của bạn nhìn vào vài byte đầu tiên của phản hồi đó. Nó mong đợi một header phiên bản TLS (như
0x16cho một quá trình bắt tay). Thay vào đó, nó thấy chữ "H" (0x48ở dạng hex) từ "HTTP". OpenSSL cố gắng phân tích cú pháp "H-T-T-P" dưới dạng số phiên bản, thất bại và đưa ra lỗiWRONG_VERSION_NUMBER.
Cách khắc phục
1. Kiểm tra URL và Cổng (Port)
Bắt đầu với nguyên nhân rõ ràng nhất: lỗi đánh máy trong chuỗi kết nối của bạn. Các môi trường phát triển cục bộ thường gặp lỗi này. Nếu ứng dụng Flask hoặc Django của bạn đang chạy trên cổng 8000, nó thường không được bật SSL theo mặc định.
- Sai:
https://127.0.0.1:8000 - Đúng:
http://127.0.0.1:8000
Nếu bạn đang nhắm tới một API production trên một cổng không tiêu chuẩn (như 8080 hoặc 8443), hãy kiểm tra kỹ xem cổng cụ thể đó có thực sự hỗ trợ HTTPS hay không. Việc một trang web có chứng chỉ SSL không có nghĩa là mọi cổng trên máy chủ đó đều được mã hóa.
2. Kiểm tra cấu hình Nginx hoặc Apache
Người dùng Nginx thường vấp phải việc thiếu một từ khóa. Nếu bạn yêu cầu Nginx lắng nghe trên cổng 443 nhưng quên yêu cầu nó sử dụng SSL, nó sẽ phục vụ HTTP thuần túy trên một cổng mà mọi người đều mong đợi là được bảo mật.
Block server Nginx của bạn nên trông như thế này:
server {
listen 443 ssl; # Từ khóa 'ssl' là phần quan trọng nhất ở đây
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
location / {
proxy_pass http://localhost:3000;
}
}
Nếu không có flag ssl đó, Nginx sẽ bỏ qua các chứng chỉ của bạn và gửi văn bản thô, gây ra lỗi ở phía client.
3. Sửa lỗi Port Mapping trong Docker
Docker thêm một lớp khác nơi các cổng có thể bị nhầm lẫn. Một sai lầm phổ biến là map một cổng bên ngoài bảo mật với một cổng container bên trong không bảo mật. Ví dụ:
# Điều này sẽ bị lỗi nếu bạn cố gắng truy cập https://localhost
docker run -p 443:80 my-web-app
Trong lệnh này, host lắng nghe trên cổng 443, nhưng nó chuyển lưu lượng truy cập đến cổng 80 bên trong container. Vì container có khả năng đang phục vụ HTTP trên cổng 80, quá trình bắt tay sẽ thất bại ngay lập tức. Hãy làm cho các giao thức khớp nhau: nếu cổng host là 443, hãy đảm bảo ứng dụng trong container thực sự được cấu hình với chứng chỉ SSL.
4. Giải quyết vòng lặp Proxy và Load Balancer
Nếu bạn đang sử dụng AWS ALB, Cloudflare hoặc một Nginx reverse proxy, bạn có thể đang gặp phải tình trạng "SSL Offloading". Đây là nơi proxy xử lý kết nối HTTPS và giao tiếp với backend của bạn qua HTTP. Nếu mã nguồn backend của bạn cố gắng ép buộc một kết nối HTTPS tới một dịch vụ nội bộ khác chỉ lắng nghe trên cổng 80, bạn sẽ gặp lỗi này.
Xác minh bản sửa lỗi
Đừng đoán—hãy kiểm tra cổng trực tiếp bằng curl. Sử dụng flag -v (verbose) để xem chính xác những gì máy chủ gửi lại trước khi lỗi xảy ra.
curl -vI https://your-api-endpoint.com
Nếu đầu ra bao gồm * error:1408F10B:SSL routines:ssl3_get_record:wrong version number, máy chủ chắc chắn đang gửi văn bản thuần túy. Bạn cũng có thể sử dụng OpenSSL để xem phản hồi thô:
openssl s_client -connect your-api-endpoint.com:443
Nếu bạn thấy CONNECTED(00000003) theo sau là một trạng thái treo hoặc một lỗi HTTP văn bản thuần túy, cấu hình máy chủ của bạn mới là vấn đề, không phải mã Python của bạn.
Mẹo phòng ngừa
Các lỗi cấu hình thường ẩn nấp trong các tệp YAML hoặc JSON lộn xộn. Tôi nhận thấy rằng việc sử dụng một trình xác thực như YAML ↔ JSON Converter này giúp phát hiện các vấn đề về cấu trúc trong Docker Compose hoặc Kubernetes manifest trước khi chúng được đưa lên production.
- Biến môi trường: Đừng bao giờ hardcode
https://. Hãy sử dụng các biến để bạn có thể chuyển đổi giữahttpkhi kiểm tra cục bộ vàhttpscho production. - Hãy tường minh: Khi debug, hãy luôn bao gồm cổng trong URL của bạn (ví dụ:
:443) để đảm bảo bạn không truy cập vào một giá trị mặc định mà bạn không mong muốn. - Giám sát Header: Sử dụng header
X-Forwarded-Protođể cho ứng dụng của bạn biết liệu yêu cầu ban đầu có an toàn hay không, ngay cả khi proxy đang giao tiếp với nó qua HTTP.

