All major webhook providers require a valid TLS/SSL certificate on the receiving endpoint. If your certificate is expired, self-signed, missing the intermediate chain, or issued for the wrong hostname, the provider will refuse to deliver webhooks and log an SSL error.
Common SSL Errors
| Error Message | Cause |
|---|---|
certificate has expired | Cert expired — renew immediately |
unable to get local issuer certificate | Missing intermediate/chain certificate |
self-signed certificate | Self-signed cert — providers reject these |
certificate subject name does not match | Cert issued for different hostname |
certificate is not yet valid | Server clock wrong — check NTP sync |
Diagnose With OpenSSL
# Check certificate expiry and chain
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
| openssl x509 -noout -dates -subject -issuer
# Check full chain (should show 2-3 certs)
openssl s_client -connect yourdomain.com:443 -showcerts 2>/dev/null \
| grep -E "(subject|issuer|notAfter)"
# Simulate what providers check
curl -v https://yourdomain.com/webhook 2>&1 | grep -E "(SSL|TLS|certificate)"
Fix 1: Get a Free Certificate with Let's Encrypt
# Install certbot (Ubuntu/Debian)
apt install certbot python3-certbot-nginx
# Issue certificate for your domain
certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Enable auto-renewal via systemd timer
systemctl enable certbot.timer && systemctl start certbot.timer
# Test renewal
certbot renew --dry-run
Fix 2: Fix Missing Intermediate Chain (nginx)
If you installed the cert manually, you may only have the leaf certificate without the intermediate chain. Concatenate them:
# Order: leaf cert first, then intermediate
cat /etc/ssl/certs/yourdomain.crt /etc/ssl/certs/intermediate.crt \
> /etc/ssl/certs/yourdomain-fullchain.crt
# nginx config
ssl_certificate /etc/ssl/certs/yourdomain-fullchain.crt;
ssl_certificate_key /etc/ssl/private/yourdomain.key;
nginx -t && systemctl reload nginx
Fix 3: Hostname Mismatch
If the webhook URL is https://api.yourdomain.com/webhook but the certificate only covers yourdomain.com, add the subdomain to the certificate:
certbot --nginx -d yourdomain.com -d api.yourdomain.com -d www.yourdomain.com
Fix 4: Self-Signed Certificate
Self-signed certificates are rejected by all webhook providers — in production and in testing. For local development, use WebhookWhisper to get a provider-trusted HTTPS URL that forwards to your localhost handler. No certificate needed on your end.
FAQ
My certificate is valid but the provider still reports an SSL error — why?
Run openssl s_client -connect yourdomain.com:443 -showcerts. If you see only one certificate in the output, the intermediate chain is missing. Providers validate the full chain to a trusted root, not just the leaf certificate.
How do I test webhooks locally without a TLS certificate?
Use WebhookWhisper to get a trusted public HTTPS URL and forward incoming webhooks to http://localhost:3000. No TLS is required on your local server — WebhookWhisper handles the provider-facing HTTPS.
How often should I renew Let's Encrypt certificates?
Let's Encrypt certificates expire after 90 days. Certbot's systemd timer renews automatically when fewer than 30 days remain. Without the timer, add a cron job: 0 0,12 * * * certbot renew --quiet.