SSL: CERTIFICATE_VERIFY_FAILED / unable to get local issuer certificate
The client couldn’t verify the server’s TLS certificate chain, so it refused the HTTPS connection.
Meaning
TLS clients check that the server certificate chains up to a trusted root CA, matches the hostname and isn’t expired. The error means one of those checks failed — very often the server is missing its intermediate certificate, or the client machine has an outdated / missing CA bundle (common with Python on macOS and behind corporate SSL-inspecting proxies).
Turning verification off makes the error go away but removes protection against man-in-the-middle attacks — fix the chain or the trust store instead.
Common causes
- Server sends only its leaf certificate, without the intermediate chain
- Client CA bundle missing/outdated (Python.org installer on macOS, old Docker base images)
- Corporate proxy re-signs HTTPS with its own root CA that the client doesn’t trust
- Self-signed certificate in development
- Certificate expired or hostname doesn’t match (
*.example.comdoesn’t coverexample.com) - System clock badly wrong
⚡ Quick fix
- Test the server chain:
openssl s_client -connect host:443 -servername host -showcerts - On the server, install the full chain (fullchain.pem, not cert.pem)
- Update client CA certificates (
pip install -U certifi,apt install ca-certificates) - macOS + python.org Python: run “Install Certificates.command”
- Behind a corporate proxy, point the client at the company root CA
Detailed fix by platform
Python
- Use certifi’s bundle or your company CA:python
import certifi, requests requests.get("https://api.example.com", verify=certifi.where()) # corporate proxy root CA: requests.get("https://api.example.com", verify="/etc/ssl/certs/corp-root.pem")
Node.js
- Trust an extra CA (corporate proxy) without disabling verification:bash
export NODE_EXTRA_CA_CERTS=/etc/ssl/certs/corp-root.pem node app.js # never ship NODE_TLS_REJECT_UNAUTHORIZED=0 to production
Nginx
- Serve the full chain:nginx
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # not cert.pem ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
PHP
- cURL error 60: set
curl.cainfo/openssl.cafilein php.ini to a current cacert.pem.
Git
git config --global http.sslCAInfo /path/to/corp-root.peminstead ofhttp.sslVerify false.
Code examples
Check the chain the server sends
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
| grep -E 'Verify return code|s:|i:'
# "Verify return code: 21 (unable to verify the first certificate)" → intermediate missing on the serverHow to diagnose
- Server chain — Does openssl s_client show the full chain and return code 0?
- Hostname & dates — Does the cert match the host and is it within its validity period?
- Client trust store — Is the CA bundle current? Which one does this runtime use?
- Network — Is a proxy/antivirus intercepting TLS?
- Clock — Is the system time correct?
🧠 Still stuck? Analyze your error
Paste the full message, response headers or stack trace — we'll detect the platform and point to the most likely cause.
Report a correction or suggest an improvement
Last updated 2 Oct 2026