Telegram logoTelegram
Bot Setup
Webhook
SSL
Cert
Deploy
Bot API
HTTPS

Step-by-Step Telegram Bot SSL Certificate Guide

Telegram Technical Team
January 12, 2026
Telegram Bot webhook SSL, Telegram Bot HTTPS certificate, setWebhook SSL certificate, Telegram Bot webhook tutorial, how to secure Telegram webhook, Lets Encrypt Telegram Bot, webhook certificate verification, Telegram Bot API HTTPS, troubleshoot Telegram webhook SSL, renew webhook certificate
Secure your Telegram bot webhook fast with this SSL certificate guide: pick cert type, generate CSR, install on server, test with Bot API 7.8, avoid mixed-content traps.

Why SSL Still Matters for Telegram Bots in 2026

Telegram Bot API 7.8 will silently drop any update that reaches its endpoint over plain HTTP. That single line in the January 2026 changelog buried dozens of hobby bots overnight. If your webhook URL does not present a valid, publicly trusted X.509 certificate, the platform answers with "SSL error" and retries stop after three attempts. The pain is instant: no logs on Telegram’s side, 100 % message loss, and your server keeps answering 200 OK to thin air.

This guide walks you through the shortest reproducible path—from zero certificate to green checkmark—while calling out the corners where operators still get stuck (IPv6-only hosts, CDN masking, alt-port tunnels, or Let’s Encrypt rate limits). Everything is CLI-first so you can embed it in CI; GUI lovers can adapt the same CSR and key with any panel.

Core Terms in One Breath

Webhook = the HTTPS URL you register once with setWebhook; CSR = Certificate Signing Request containing your public key; PEM = text file that stores key or cert; chain = intermediate plus root stitched together; Bot API 7.8 = current stable layer that enforces TLS 1.3 and rejects certificates shorter than 2048-bit RSA or 256-bit EC.

Pick the Right Certificate Type

DV (Domain Validated) – Let’s Encrypt

Free, 90-day life, automatic renewal via certbot. Ideal if you control the server OS and can expose port 80. Telegram accepts the ISRG root since 2020.

Wildcard or Multi-Domain

Necessary when the same bot answers on bot.example.com and status.example.com. Let’s Encrypt offers wildcards via DNS-01 challenge; budget 5 min for API credentials at your DNS vendor.

Cloud-Provider Managed

AWS ACM, Google Managed, Azure Front Door. You never see the private key, but you must place the load balancer in front of your container. Telegram accepts these certs; just be sure the balancer forwards the original Host header.

Prerequisites Checklist

  • A domain name that you can set an A (or AAAA) record on.
  • Server with root shell, 512 MB RAM, any modern Linux.
  • Nginx or Caddy already serving default page on 443 (can be dummy).
  • Bot token copied from BotFather → /mybots → [Bot] → API Token.
  • OpenSSL 3.x (check with openssl version).
Warning: Telegram follows CAs in the Mozilla bundle. Self-signed certificates work only for the deprecated setWebhook parameter [email protected] while using the *self-signed* upload flow. That flow breaks in Bot API 7.8 if your cert is not SHA-256 and 2048-bit+. You will save hours by using a real CA instead of fighting the exception path.

Generate Private Key and CSR

SSH to the box and run:

sudo mkdir -p /etc/ssl/bot && cd /etc/ssl/bot
sudo openssl req -new -newkey rsa:2048 -nodes -keyout bot.key -out bot.csr -subj "/CN=bot.example.com"

You can swap rsa:2048 for ec:param:secp256r1 to get a smaller EC key; Telegram accepts both. Keep bot.key at 0600 permissions.

Obtain the Certificate

Option A – Certbot (HTTP-01)

  1. Temporarily stop any service on port 80 or allow temporary stand-alone mode:
sudo certbot certonly --standalone -d bot.example.com --preferred-chain "ISRG Root X1"

Certificates land in /etc/letsencrypt/live/bot.example.com/. A systemd timer will renew them; append --post-hook "nginx -s reload" to automate reload.

Option B – DNS-01 (for wildcards or internal servers)

sudo certbot certonly --dns-cloudflare -d "*.example.com" -d example.com \n  --credentials ~/.secrets/cloudflare.ini

DNS propagation time is your main variable; budget 2-3 min. Once issued, the rest of the flow is identical.

Install Certificate in Nginx

Edit /etc/nginx/sites-available/bot:

server {
    listen 443 ssl http2;
    server_name bot.example.com;
    ssl_certificate /etc/letsencrypt/live/bot.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/bot.example.com/privkey.pem;
    ssl_protocols TLSv1.3;
    ssl_ciphers TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384;
    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Reload:

sudo nginx -t && sudo systemctl reload nginx

Tell Telegram Where to Deliver

One curl call:

curl -F "url=https://bot.example.com/telegram" \n  https://api.telegram.org/bot<TOKEN>/setWebhook

If you need a custom port (8443, 8843, 9443), append -F "ip_address=YOUR.IP"; otherwise Telegram resolves A/AAAA and verifies port 443. A successful response contains "ok":true and the field "result":{}.

Verify the Hook Is Actually Secure

  1. Check cert chain locally:
openssl s_client -connect bot.example.com:443 -servername bot.example.com \n  </dev/null | grep "Verify return code"

Expected: Verify return code: 0 (ok).

  1. Ask Telegram for its view:
curl https://api.telegram.org/bot<TOKEN>/getWebhookInfo

If "last_error_message" shows "SSL error", the cert is still untrusted; if "pending_update_count" drops to zero, you are done.

Troubleshooting Matrix

SymptomLikely CauseQuick Check
getWebhookInfo shows "SSL error”Chain missing intermediateopenssl s_client displays “Extra download”
Updates arrive but stop after 3 triesHTTP 301/302 redirectcurl -I follows Location header
Nginx serves old cert after renewalNo reload post-hookjournalctl -u certbot
Telegram rejects EC certificateKey too small (<secp256r1)openssl x509 -text -noout

Platform-Specific Paths (When You Prefer a UI)

Android 11.0

BotFather → search → tap your bot → Bot Settings → Webhook → paste URL. No file upload anymore; the app only accepts https:// links.

Desktop 5.12 (macOS & Win)

Same flow, but you can drag-and-drop the public PEM if you insist on the self-signed exception (again, not recommended post-7.8).

When Not to Use Your Own Certificate

  • You run the bot behind a serverless function (Vercel, Netlify) that already provides a managed cert. In that case simply deploy the function and register its https:// URL—no CSR needed.
  • Internal prototypes that never leave your laptop. Use the polling mode (getUpdates) instead; no webhook means no certificate.
  • High-compliance environments that require FIPS-validated hardware modules. You can still use Telegram’s webhook, but the key must live in an HSM and Nginx needs the ssl_engine directive—out of scope for a basic guide.

Mini Case: 10 k rps Game Bot

An indie game studio ran a contest bot that peaked at 10 000 updates per second. They started with a single $5 VPS and a Let’s Encrypt cert. At 3 k rps the CPU spent 25 % on TLS handshakes. Moving the certificate into a nearby AWS ACM + CloudFront cut handshake latency by 40 % and freed the VPS to handle game logic. Lesson: once your webhook TTFB exceeds 200 ms, offloading TLS to a CDN becomes cheaper than horizontal scaling.

Version Differences & Migration Notes

Bot API 6.x still accepted self-signed certs with a simple upload. API 7.0 (Aug 2024) introduced TLS 1.3 enforcement and removed SHA-1. API 7.8 (Jan 2026) tightened key length and revoked trust in the “Digital Signature Trust Co.” legacy root. If you migrated servers recently, double-check the CA bundle embedded in old configuration management playbooks.

Best-Practice Checklist

  1. Use fullchain.pem to avoid missing intermediates.
  2. Set ssl_protocols TLSv1.3; Telegram no longer negotiates 1.2.
  3. Enable http2 for header compression; Bot API accepts it.
  4. Schedule certbot renew daily, not weekly; Let’s Encrypt may revoke certs within 24 h if a vulnerability appears.
  5. After every renewal, run getWebhookInfo and assert "last_error_date" is null—automate in CI.
  6. Keep the private key outside web root with 0600 permissions.
  7. Document the port-forwarding rule in your infra repo; future you will forget 8443 vs 443.

Future-Proofing: What Might Change Next

Telegram has hinted at Ed25519 certificate support in Bot API 8.0 (expected Q3 2026). If you rotate keys through a configuration management tool, parameterize the algorithm now so you can swap secp256r1 for ed25519 with one variable change. Also expect stricter SNI mismatch rejection: the webhook domain must already live in the cert’s SAN list—no more wildcard “*” fallback.

Key Takeaway

A valid SSL certificate is no longer a nice-to-have; it is the gatekeeper between your code and 900 million Telegram users. Obtain it automatically, chain it correctly, and verify from both sides—then you can focus on features instead of phantom message loss.

Case Study #1 – Community Moderation Bot (500 guilds, 1 VM)

Context: An open-source moderation bot serves ~500 Telegram groups from a single 2 vCPU VM.
Challenge: After the Bot API 7.8 release, updates vanished; the operator had used a 1024-bit self-signed cert.
Remedy: Migrated to Let’s Encrypt DNS-01 (the VM lacks port 80), added a daily cron renewal, and pinned the preferred chain to ISRG Root X1.
Result: Updates resumed within 15 min; CPU overhead rose <1 %. The operator later parameterized the CSR in Ansible, so new staging bots inherit the same flow.

Case Study #2 – E-commerce Notification Bot (Global, 50 k rps)

Context: A retail giant delivers order alerts to 4 M users; traffic spikes at 50 k rps during flash sales.
Challenge: TLS handshakes saturated their regional Nginx fleet, causing 95th-percentile latency to exceed 600 ms.
Remedy: Shifted the public-facing endpoint to Google Cloud Load Balancer with Google-managed certs, kept EC256 keys, and enabled QUIC (gCLB exposes it by default).
Result: P99 latency dropped to 120 ms; origin servers saw 70 % fewer TLS negotiations. The team also gained automatic GEO-edge renewal with zero downtime.

Runbook: Monitoring & Rollback

1. Alerting Signals

Webhook downtime is invisible to your app logs. Create alerts on:

  • pending_update_count > 100 for 2 min (poll getWebhookInfo)
  • Cert expiry ≤ 7 days (Prometheus exporter ssl_exporter)
  • Nginx 5xx rate > 1 % on the bot location

2. Localisation Rapide

  1. Run curl -i https://api.telegram.org/bot<TOKEN>/getWebhookInfo – note last_error_message.
  2. Test your chain: openssl s_client -servername ... -showcerts.
  3. Check renewal logs: journalctl -u certbot -n 100.
  4. If the cert is good but Telegram still errors, verify SNI match: the domain in your setWebhook call must appear in the cert’s SAN.

3. Rollback / Mitigation

If a bad cert is served and renewal hooks failed:

sudo certbot rollback --checkpoints 1
sudo systemctl reload nginx

Still broken? Temporarily switch your bot to polling:

curl -F "url=" https://api.telegram.org/bot<TOKEN>/setWebhook

…and restart your app in getUpdates mode. You lose real-time delivery but stay online.

4. Quarterly Drill

  • Stage a dummy subdomain, break the chain on purpose, measure MTTR.
  • Document who gets paged, where the runbook lives, and the exact rollback commands.
  • Capture screenshots of getWebhookInfo before/after; attach to post-mortem.

FAQ – Quick Fire

Q: Can I use a free .tk / .ml domain?
A: Yes, Telegram only validates the certificate, not the TLD reputation. Evidence: community bots on .tk domains work once a valid Let’s Encrypt cert is in place.
Q: IPv6-only host – any gotchas?
A: Telegram’s crawlers are dual-stack. Ensure your AAAA record is reachable and that certbot validation (HTTP-01 or DNS-01) prefers IPv6. Some DNS providers still answer IPv4 first; test with dig bot.example.com AAAA.
Q: Alt-port 8443 fails – why?
A: You must explicitly pass ip_address=YOUR.IP in setWebhook when the port is not 443, 80, 88, or 8443. Background: Telegram maintains a whitelist to reduce port-scan noise.
Q: Does Cloudflare’s “Flexible SSL” work?
A: No. Flexible SSL means Cloudflare connects to your origin over HTTP. Telegram requires end-to-end HTTPS; use “Full (strict)” instead and install a Cloudflare origin cert on your server.
Q: Can I reuse the same cert for Matrix, XMPP, and my bot?
A: Yes, if the SAN list covers all hostnames. Let’s Encrypt allows up to 100 names per cert; wildcards simplify the count.
Q: Rate limits?
A: Let’s Encrypt: 5 duplicate certs per week per domain set. Plan staging vs production hostnames to avoid lockouts. Telegram itself imposes no rate limit on setWebhook calls, but don’t script-loop it needlessly.
Q: Is HSTS required?
A: Not mandated by Telegram, but adding add_header Strict-Transport-Security "max-age=63072000"; hardens your domain against downgrade attacks with zero downside.
Q: Can I pin my intermediate for speed?
A: Telegram does not support static public-key pinning; always serve the full chain. Omitting intermediates will raise “SSL error”.
Q: What cipher suite is the fastest?
A: On x86_64, AES-256-GCM with hardware acceleration (AES-NI) shows negligible CPU difference versus 128-bit. Stick to the official TLS 1.3 ciphers listed in the sample config.
Q: Certbot post-hook not firing in LXC – fix?
A: Ensure the container has systemd-timer or fallback to crond. Add explicit path: --post-hook "/usr/sbin/nginx -s reload" because $PATH inside timers may be minimal.

Terminology Recap

TermDefinitionFirst seen
WebhookHTTPS endpoint Telegram calls for every updateIntroduction
CSRCertificate Signing Request containing your public key and domain nameGenerate Private Key
PEMBase-64 encoded text format for keys / certs / chainsCore Terms
ChainIntermediate + root certificates stitched into one fileCore Terms
DVDomain Validated certificate (only proves domain control)Pick the Right Certificate
ISRG Root X1Let’s Encrypt root CA now trusted by all major platformsObtain Certificate
DNS-01Challenge type that proves ownership via a TXT recordObtain Certificate
HTTP-01Challenge type that serves a token on port 80Obtain Certificate
SNIServer Name Indicator sent by client to select correct certTroubleshooting
SANSubject Alternative Name field listing all valid domainsWildcard
HSMHardware Security Module storing keys in tamper-proof siliconWhen Not to Use
TTFBTime to First Byte (latency metric)Mini Case
QUICUDP-based encrypted transport with 0-RTT resumeCase #2
Full (strict)Cloudflare mode demanding valid cert on originFAQ
MTTRMean Time To Repair after failureDrill
Polling modePeriodic getUpdates calls instead of webhook pushesWhen Not to Use
Root CATop-level certificate in trust store that signs intermediatesVersion Differences

Risk & Boundary Matrix

ScenarioRisk / LimitationWorkaround / Alternative
Shared hosting without shellCannot run certbotBuy a 1-year DV from vendor or move to a panel that offers AutoSSL (e.g., cPanel).
Internal RFC 5737 networksLet’s Encrypt refuses private rangesUse DNS-01 challenge or an internal CA plus polling mode.
FIPS-only complianceLet’s Encrypt not FIPS-validatedPurchase a cert from a FIPS-root CA and store key in HSM.
Wildcard with frequent subdomain spin-upDNS-01 propagation lagAutomate via your DNS vendor’s instant-API; budget 60 s TTL.
Bot running on GCP Cloud RunGoogle manages certs but hides private keyNo action needed; just register the https:// URL provided by Cloud Run.

Next Version Outlook

Ed25519 signatures, SNI hardening, and possibly mandatory Certificate Transparency logs are on the horizon. Automate your CSR pipeline today, and you’ll swap algorithms with a single variable change tomorrow. Until then, keep your chain complete, your port 443 open, and your getWebhookInfo on speed dial.