Cách khắc phục lỗi [SSL: CERTIFICATE_VERIFY_FAILED] trong Python

intermediate🌐 Networking2026-04-25| Python 3.x (Requests, urllib, aiohttp), macOS, Windows, hoặc Linux đằng sau proxy doanh nghiệp.

Error Message

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
#ssl#python#api#mạng#openssl

Bối cảnhÍt có điều gì làm đình trệ một dự án Python nhanh hơn là lỗi SSL handshake đột ngột. Bạn có thể đang gọi một REST API như Stripe hoặc Twilio, và trong khi mọi thứ hoạt động bình thường trên máy Linux cục bộ, script lại bị crash ngay khi bạn chuyển sang macOS hoặc một mạng doanh nghiệp bị hạn chế. Điều này xảy ra vì Python không thể tìm thấy bộ chứng chỉ gốc (root certificates) hợp lệ để xác minh danh tính của máy chủ mà bạn đang cố gắng kết nối.

Hãy coi đó như một chốt kiểm soát an ninh. Script của bạn nhìn thấy thẻ ID của máy chủ (chứng chỉ SSL), nhưng nó không nhận ra cơ quan đã cấp thẻ đó. Nếu không có danh sách các cơ quan đáng tin cậy tại địa phương, Python sẽ từ chối kết nối để ngăn chặn rò rỉ bảo mật tiềm ẩn.

Môi trường & Thông báo lỗiLỗi này phổ biến nhất trên Python 3.6+ chạy trên macOS Ventura hoặc Sonoma. Nó cũng gây khó khăn cho các nhà phát triển trên Windows hoặc Linux khi làm việc sau tường lửa doanh nghiệp—như Zscaler hoặc Cisco Umbrella—những hệ thống thực hiện kiểm tra SSL bằng cách ký lại lưu lượng truy cập bằng các chứng chỉ nội bộ.

Thông báo lỗi chính xác thường trông như thế này:

urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1123)>

Gỡ lỗi nguyên nhân gốc rễTrước khi thử mọi cách sửa lỗi trên StackOverflow, hãy kiểm tra chính xác nơi Python đang tìm kiếm kho lưu trữ tin cậy (trust store). Chạy đoạn mã này trong terminal hoặc IDE của bạn:

import ssl
import os

print("Default Verify Paths:", ssl.get_default_verify_paths())
print("SSL_CERT_FILE:", os.environ.get('SSL_CERT_FILE'))
print("SSL_CERT_DIR:", os.environ.get('SSL_CERT_DIR'))

Nếu openssl_cafileNone hoặc trỏ đến một đường dẫn không tồn tại, bạn đã tìm thấy vấn đề. Về cơ bản, Python đang bị "mù" thông tin.

Giải pháp### 1. Cách khắc phục riêng cho macOSNếu bạn cài đặt Python thông qua trình cài đặt chính thức từ python.org, nó sẽ không sử dụng hệ thống keychain của macOS. Thay vào đó, nó sử dụng phiên bản OpenSSL nội bộ đi kèm với con số không chứng chỉ. Bạn có thể khắc phục điều này trong năm giây bằng cách chạy script lệnh đi kèm với bản cài đặt của mình:

# Thay đổi 3.11 để khớp với phiên bản cụ thể của bạn
/Applications/Python\ 3.11/Install\ Certificates.command

Script này cài đặt gói certifi và tạo một liên kết tượng trưng (symbolic link) để Python biết nơi tìm kiếm bộ chứng chỉ.

2. Cập nhật Certifi và Biến môi trườngĐôi khi bộ chứng chỉ cục bộ của bạn đã quá cũ để nhận diện các Cơ quan cấp chứng chỉ (CAs) mới hơn. Thư viện certifi cung cấp một bộ sưu tập các Root Certificates được tuyển chọn và cập nhật liên tục. Hãy bắt đầu bằng cách tải phiên bản mới nhất:

pip install --upgrade certifi

Bây giờ, hãy buộc Python sử dụng bộ chứng chỉ cụ thể này bằng cách thiết lập các biến môi trường trong tệp cấu hình shell của bạn (.bashrc hoặc .zshrc):

export SSL_CERT_FILE=$(python -m certifi)
export REQUESTS_CA_BUNDLE=$(python -m certifi)

3. Xử lý Proxy doanh nghiệp (Cách dành cho doanh nghiệp)Trong môi trường doanh nghiệp, công ty của bạn có khả năng chặn lưu lượng SSL để quét các mối đe dọa. certifi sẽ không hoạt động ở đây vì nó không biết về Root CA riêng của công ty bạn. Bạn cần thêm chứng chỉ của công ty vào kho lưu trữ tin cậy một cách thủ công. Đầu tiên, hãy xuất chứng chỉ doanh nghiệp của bạn dưới dạng tệp .pem, sau đó nối nó vào bộ chứng chỉ hiện có:

# Tìm đường dẫn đến bộ certifi hiện tại của bạn
python -m certifi
# Ví dụ đầu ra: /usr/local/lib/python3.11/site-packages/certifi/cacert.pem

# Nối chứng chỉ doanh nghiệp của bạn vào cuối tệp
cat company_root.pem >> /path/to/your/certifi/cacert.pem

4. Cách khắc phục "Tạm thời" (Không an toàn)Nếu bạn đang thử nghiệm một máy chủ cục bộ với chứng chỉ tự ký và bảo mật không phải là ưu tiên hàng đầu, bạn có thể bỏ qua việc xác minh. Đừng bao giờ sử dụng mã này trong môi trường production. Nó khiến bạn hoàn toàn sơ hở trước các cuộc tấn công Man-in-the-Middle (MITM).

import requests

# Dành cho thư viện requests
response = requests.get('https://localhost:8000', verify=False)

# Dành cho ngữ cảnh urllib toàn cục
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

Xác minh: Đã khắc phục được chưa?Chạy bài kiểm tra nhanh này để xem liệu Python hiện có thể thiết lập SSL handshake an toàn với một trang web lớn hay không:

import requests
try:
    response = requests.get('https://google.com', timeout=5)
    print(f"Success! Status code: {response.status_code}")
except Exception as e:
    print(f"Connection failed: {e}")

Những lưu ý quan trọng- Chứng chỉ không phải là phép thuật: Python trên macOS được thiết kế có chủ đích là "headless" và yêu cầu thiết lập chứng chỉ thủ công sau khi cài đặt.- Tránh sử dụng verify=False: Đây là một thói quen nguy hiểm. Hãy luôn ưu tiên cập nhật bộ CA của bạn thay vì tắt tính năng bảo mật.- Ngữ cảnh mạng rất quan trọng: Nếu bạn đang gọi các microservices nội bộ, quá trình handshake có thể thất bại do định tuyến. Nếu bạn nghi ngờ bị cô lập mạng, hãy sử dụng một Subnet Calculator để đảm bảo backend của bạn nằm trong dải CIDR thực sự có thể kết nối tới API gateway.Bằng cách cập nhật certifi thường xuyên và trỏ biến SSL_CERT_FILE đến đúng vị trí, bạn có thể giải quyết đại đa số các lỗi SSL chỉ trong vài phút.

Related Error Notes