Introduction
Self-signed SSL certificates are commonly used in development, testing, and internal environments. By default, curl and most HTTP clients reject self-signed certificates because they are not signed by a trusted Certificate Authority. While --insecure (-k) bypasses this check, it disables all certificate validation, creating a security risk. The proper approach is to add the self-signed certificate (or its CA) to the trusted certificate store.
Symptoms
curl https://internal-serverfails withSSL certificate problem: self-signed certificatecurl: (60) SSL certificate problem: self signed certificate- Python requests fails with
SSLError: certificate verify failed: self-signed certificate - Java fails with
PKIX path building failed: unable to find valid certification path - Using
-kflag works but is flagged as insecure in code reviews
Common Causes
- Internal server using self-signed certificate for HTTPS
- Development environment without a private CA infrastructure
- Certificate generated with
openssl req -x509for testing - Container or VM spun up with auto-generated self-signed cert
- Internal API service not provisioned with a proper CA-signed certificate
Step-by-Step Fix
- 1.Download the self-signed certificate from the server:
- 2.```bash
- 3.echo | openssl s_client -connect internal-server:443 -servername internal-server 2>/dev/null | \
- 4.openssl x509 -out internal-server.pem
- 5.# Verify it is self-signed
- 6.openssl x509 -in internal-server.pem -noout -issuer -subject
- 7.# Issuer and Subject should be identical for self-signed
- 8.
` - 9.Add the certificate to the system CA bundle:
- 10.```bash
- 11.# Debian/Ubuntu
- 12.sudo cp internal-server.pem /usr/local/share/ca-certificates/internal-server.crt
- 13.sudo update-ca-certificates
# RHEL/CentOS sudo cp internal-server.pem /etc/pki/ca-trust/source/anchors/ sudo update-ca-trust ```
- 1.Verify curl now trusts the certificate:
- 2.```bash
- 3.curl -vI https://internal-server 2>&1 | grep -E "SSL|HTTP"
- 4.# Should show successful SSL handshake without -k flag
- 5.
` - 6.For a single curl command, use --cacert instead of -k:
- 7.```bash
- 8.curl --cacert internal-server.pem https://internal-server
- 9.# This validates only against this specific CA, not all CAs
- 10.
` - 11.For Python requests, specify the CA bundle:
- 12.```python
- 13.import requests
- 14.response = requests.get('https://internal-server', verify='/path/to/internal-server.pem')
- 15.
` - 16.For Java, import into the truststore:
- 17.```bash
- 18.sudo keytool -import -trustcacerts \
- 19.-alias internal-server \
- 20.-file internal-server.pem \
- 21.-keystore $JAVA_HOME/lib/security/cacerts \
- 22.-storepass changeit -noprompt
- 23.
`
Prevention
- Set up an internal Certificate Authority for all internal services
- Use Let's Encrypt or internal PKI instead of self-signed certificates for production
- Automate CA bundle updates through configuration management
- Document the certificate trust setup for new team member onboarding
- For development environments, use mkcert to generate locally-trusted certificates