Každé varování prohlížeče o certifikátu je jeden z asi šesti problémů a kód chyby ti řekne, který z nich. NET::ERR_CERT_DATE_INVALID znamená, že certifikát vypršel nebo že návštěvník má špatně nastavené hodiny. NET::ERR_CERT_COMMON_NAME_INVALID znamená, že certifikát je vystavený na jiné jméno. NET::ERR_CERT_AUTHORITY_INVALID znamená, že prohlížeč nedokáže certifikát dohledat k autoritě, které věří - jde o self-signed certifikát nebo o server, který zapomněl poslat svůj intermediate. ERR_SSL_PROTOCOL_ERROR znamená, že se obě strany vůbec nedostaly k tomu, aby se na certifikát podívaly. A zámek s nápisem „Not secure“ vedle naprosto platného certifikátu je mixed content. Přečti si kód, spusť jeden příkaz openssl z počítače mimo tvou síť a oprava bývá většinou jeden řádek konfigurace.
Tento článek je ta řešící část tématu. Jak se certifikáty vystavují a obnovují, najdeš v článku HTTPS a Let's Encrypt jednoduše a jak namířit doménu na server, aby se certifikát vůbec dal vystavit, v článku namiř doménu na server a přidej SSL.
Nejdřív si přečti kód chyby#
Velká červená stránka má návštěvníky odradit, ne ti pomoct, takže to užitečné je malé. V Chrome a Edge je to šedý kód pod vysvětlením nebo za odkazem „Advanced“. Ve Firefoxu je také za „Advanced“ a píše se jinak. Safari většinou řekne „This Connection Is Not Private“ a důvod schová za „Show Details“ a „view the certificate“.
| Chrome / Edge | Firefox | Co to znamená |
|---|---|---|
NET::ERR_CERT_DATE_INVALID | SEC_ERROR_EXPIRED_CERTIFICATE | Vypršel, ještě neplatí, nebo jsou špatně hodiny |
NET::ERR_CERT_COMMON_NAME_INVALID | SSL_ERROR_BAD_CERT_DOMAIN | Certifikát neobsahuje toto jméno |
NET::ERR_CERT_AUTHORITY_INVALID | SEC_ERROR_UNKNOWN_ISSUER | Nedůvěryhodný vydavatel nebo chybějící intermediate |
NET::ERR_CERT_AUTHORITY_INVALID | MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT | Self-signed certifikát |
ERR_SSL_PROTOCOL_ERROR | SSL_ERROR_RX_RECORD_TOO_LONG | Na tom portu není TLS, často je tam obyčejné HTTP |
ERR_SSL_VERSION_OR_CIPHER_MISMATCH | SSL_ERROR_NO_CYPHER_OVERLAP | Žádná společná verze TLS ani šifra |
NET::ERR_CERT_REVOKED | SEC_ERROR_REVOKED_CERTIFICATE | Autorita certifikát odvolala |
Když už jsi na té stránce, všimni si ještě dvou věcí. Za prvé, jestli tam je odkaz „Proceed anyway“: když není, web má nastavené HSTS (nebo je na doméně s přednastaveným HSTS, jako je cokoli pod .dev nebo .app) a prohlížeč nikomu nedovolí proklikat se dál. To je HSTS, které dělá svou práci, ne další závada. Za druhé, kdo to vidí. Varování, které dostane jen jeden člověk, je téměř vždy chyba jeho počítače - hodiny, antivirus, firemní proxy, wifi v hotelu. Varování, které dostanou všichni, je chyba serveru.
Zkontroluj zvenčí pomocí openssl s_client#
Prohlížeč má cache, uložené výjimky a vlastní kopie intermediate certifikátů, které sebral jinde. openssl s_client nic z toho nemá. Připojí se, provede handshake, vypíše přesně to, co server poslal, a řekne ti, jestli to projde ověřením. Spusť ho z počítače, který není ve stejné síti jako server, protože lokální přepsání DNS nebo záznam v souboru hosts ti klidně ukáže jiný server, než jaký vidí zbytek světa.
$ openssl s_client -connect example.com:443 -servername example.com </dev/null-servername je důležité. Posílá SNI, tedy část handshaku, která serveru hostujícímu spoustu webů říká, který certifikát chceš. Bez něj dostaneš to, co server považuje za výchozí, což je často špatný certifikát a hledáš pak problém, který neexistuje. Novější verze OpenSSL posílají SNI automaticky podle hostitele v -connect, starší ne, takže ho zadávej vždy. </dev/null ukončí spojení hned po handshaku, místo aby čekalo, až napíšeš HTTP požadavek.
Výstup je dlouhý. Tři části, na kterých záleží:
Certificate chain 0 s:CN = example.com i:C = US, O = Let's Encrypt, CN = R11 1 s:C = US, O = Let's Encrypt, CN = R11 i:C = US, O = Internet Security Research Group, CN = ISRG Root X1...Verify return code: 0 (ok)Řetězec vypisuje to, co server poslal: 0 je leaf (tvůj certifikát), s: je subjekt a i: vydavatel. Vydavatel každého certifikátu by měl být subjektem toho dalšího a řetězec má končit u něčeho, čemu prohlížeč věří. Názvy intermediate certifikátů se časem mění, takže si nedělej starosti, když tvoje nejsou R11; starosti si dělej, když tam je jen řádek 0. Poslední řádek je verdikt. 0 (ok) znamená, že řetězec projde proti úložišti důvěryhodných certifikátů tvého systému. Cokoli jiného je chyba vyjádřená slovy OpenSSL.
Abys viděl data a jména bez zbytečností, pošli certifikát do openssl x509:
$ openssl s_client -connect example.com:443 -servername example.com </dev/null \ 2>/dev/null | openssl x509 -noout -subject -issuer -dates -ext subjectAltName-ext vyžaduje OpenSSL 1.1.1 nebo novější. Na starším systému použij -text a blok X509v3 Subject Alternative Name si najdi očima.
Ověřovací kódy, které opravdu uvidíš:
| Kód | Zpráva | Význam |
|---|---|---|
10 | certificate has expired | Obnova selhala nebo nebyla nikdy nastavená |
18 | self-signed certificate | Leaf podepsaný sám sebou |
19 | self-signed certificate in certificate chain | Privátní CA, které klient nevěří |
20 | unable to get local issuer certificate | Neúplný řetězec, nebo chybí úložiště důvěryhodných certifikátů |
21 | unable to verify the first certificate | Server poslal leaf bez intermediate |
62 | hostname mismatch | Jméno není v certifikátu (s -verify_hostname) |
Pozor, s_client ve výchozím stavu jméno nekontroluje - u platného certifikátu patřícího úplně jiné doméně klidně napíše 0 (ok). Přidej -verify_hostname example.com, aby kontrolu provedl, nebo si výstup subjectAltName přečti sám.
NET::ERR_CERT_DATE_INVALID: vypršelo, nebo jsou špatně hodiny#
Certifikát má datum „not before“ a „not after“ a mimo toto okno je neplatný. Mimo něj se dá skončit dvěma velmi odlišnými způsoby.
Certifikát opravdu vypršel. Pokud openssl x509 -dates ukazuje notAfter v minulosti, obnova selhala. U certifikátů Let's Encrypt s platností 90 dní to znamená, že se něco asi měsíc potichu rozbíjí, protože klienti se normálně obnovují s třicetidenní rezervou. Obvyklé příčiny jsou stejné, které brání vystavení hned na začátku: doména už nemíří na server, port 80 zavřela změna ve firewallu, před origin se zapnul CDN, nebo se nikdy nenainstaloval časovač obnovy, protože první certifikát byl získán ručně. Zkontroluj log obnovy - u Certbotu je to /var/log/letsencrypt/letsencrypt.log - a spusť certbot renew --dry-run, abys selhání vyvolal na požádání.
Druhá past u vypršelého certifikátu: obnova proběhla a nic se nenačetlo znovu. Certbot zapíše nové soubory, ale nginx a většina dalších serverů čtou certifikáty jednou při startu a z paměti dál servírují ten starý, dokud se nenačtou znovu. Pokud má soubor na disku čerstvé datum a s_client ukazuje starý certifikát, je to tvůj problém. Natrvalo to spraví deploy hook:
$ certbot renew --deploy-hook "systemctl reload nginx"Kratší platnost certifikátů se blíží napříč celým odvětvím, nejen u Let's Encrypt - CA/Browser Forum se dohodlo, že maximum se v následujících letech bude snižovat a skončí na 47 dnech v roce 2029. Cokoli, co obnovuje člověk s připomínkou v kalendáři, se bude rozbíjet každým rokem častěji. Pokud je nějaký certifikát na tvém systému obnovován ručně, automatizuj nejdřív ten.
Návštěvník má špatné hodiny. Pokud chybu vidí jen jeden člověk a jeho prohlížeč píše něco jako „Your clock is behind“, certifikát je v pořádku a jeho počítač si myslí, že je jiný rok. Stává se to na strojích s vybitou baterií na základní desce, v dual-bootu, kde se Windows a Linux neshodnou, jestli je hardwarový čas místní, nebo UTC, a na virtuálních strojích obnovených ze starého snapshotu. Oprava systémového času spraví všechny weby najednou. Provozovatel webu to spravit nemůže a neměl by se o to ani pokoušet.
Ještě neplatí. Vzácnější, ale stává se: nový certifikát, jehož notBefore je o pár sekund v budoucnosti vůči hodinám serveru nebo klienta, které se rozcházejí. Nech na serverech běžet NTP - timedatectl by měl hlásit System clock synchronized: yes.
NET::ERR_CERT_COMMON_NAME_INVALID: certifikát je na jiné jméno#
Jméno v adresním řádku se musí objevit v seznamu Subject Alternative Name v certifikátu. Prohlížeče staré pole Common Name ignorují úplně - Chrome ho přestal číst v roce 2017 - takže certifikát, který má jméno jen v CN, selže, i když na první pohled vypadá správně. To postihuje hlavně domácí certifikáty; každá veřejná autorita dává jména do seznamu SAN.
Nejčastější způsoby, jak se sem dostat:
- `www` a apex jsou různá jména. Certifikát na
example.comnepokrýváwww.example.coma naopak. Buď získej jeden certifikát s oběma jmény, nebo jedno přesměruj na druhé - a samotné přesměrování potřebuje platný certifikát na jméně, ze kterého přesměrovává, protože prohlížeč kontroluje certifikát dřív, než přesměrování vůbec uvidí. Jak vybrat, které jméno bude hlavní, popisuje www vs apex doména a přesměrování. - Wildcard pokrývá přesně jednu úroveň.
*.example.compokrýváshop.example.com, ale neexample.coma neapi.shop.example.com. Wildcard certifikáty, které „nefungují“, se téměř vždy snažíš použít pro jedno z těchto dvou. - Žádná shoda SNI na sdíleném serveru. Když server nemá pro požadované jméno nastavený certifikát, sáhne po výchozím - a ten patří nějakému jinému webu na stejném stroji. V nginx to znamená, že požadavek skončil v bloku
server, jehožserver_nametvého hostitele neobsahuje, takže odpověděl první bloklisten 443 ssl(nebo ten označenýdefault_server). Zkontrolujserver_namekvůli překlepům a spusťnginx -T, abys viděl konfiguraci, kterou nginx skutečně načetl, a ne soubor, o kterém si myslíš, že jsi ho upravil. Funkční uspořádání najdeš v průvodci reverzní proxy v nginx. - DNS míří na špatný stroj. Přestěhoval jsi web, starý záznam A tam pořád je nebo je pořád v cache a návštěvníci se dostávají na předchozí server, který má certifikát na něco úplně jiného. Porovnej
dig +short example.com @1.1.1.1s adresou, kterou čekáš. Související záznamy vysvětluje DNS záznamy jednoduše. - Návštěva přes IP adresu.
https://203.0.113.10nikdy neodpovídá certifikátu na doménu. Použij jméno.
Neúplný řetězec: v tvém prohlížeči funguje, jinde ne#
Tahle chyba zabere nejvíc času, protože ji ten, kdo server nastavoval, nedokáže zopakovat. Certifikát je platný, jméno sedí, data jsou v pořádku a web se v jeho prohlížeči načte. Mezitím curl selže s curl: (60) SSL certificate problem: unable to get local issuer certificate, skript v Pythonu vyhodí CERTIFICATE_VERIFY_FAILED, webhook platební brány odmítá doručovat a některé telefony ukazují varování.
Veřejná autorita nepodepisuje tvůj certifikát svým kořenovým klíčem, který je uložený offline. Podepisuje intermediate certifikátem a ten je podepsaný kořenem. Prohlížeče a operační systémy kořeny obsahují; intermediate nemusí mít. Server proto musí poslat oba: nejdřív tvůj certifikát, pak intermediate. Pošli jen první a klient drží certifikát podepsaný někým, o kom nikdy neslyšel.
Desktopové prohlížeče tuhle chybu často skryjí. Chrome a další si chybějící intermediate stáhnou podle URL uvnitř certifikátu, nebo použijí ten, který mají v cache z jiného webu, kde byl poslán správně. Nástroje příkazové řádky, HTTP knihovny programovacích jazyků a mnoho dalších klientů to nedělají a selžou. s_client to ukáže okamžitě: v řetězci je jen řádek 0 a Verify return code: 21 (unable to verify the first certificate).
Oprava je servírovat soubor s celým řetězcem místo samotného certifikátu. U Certbotu to znamená fullchain.pem místo cert.pem:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;Apache 2.4.8 a novější čtou řetězec ze stejného souboru, takže i SSLCertificateFile má mířit na fullchain.pem; stará direktiva SSLCertificateChainFile je zastaralá. U certifikátu koupeného od komerční autority obvykle dostaneš certifikát a soubor „bundle“ nebo „CA bundle“ zvlášť. Spoj je za sebe, nejdřív svůj certifikát:
$ cat example_com.crt ca-bundle.crt > fullchain.crtNa pořadí záleží. Pokud je intermediate první, nginx si bude stěžovat, že klíč neodpovídá certifikátu, protože klíč porovnává s prvním certifikátem v souboru. Kořen sám nepřidávej - klienti ho už mají a jeho posílání jen zvětšuje každý handshake.
Self-signed certifikáty a NET::ERR_CERT_AUTHORITY_INVALID#
Self-signed certifikát je podepsaný vlastním klíčem. Provoz šifruje stejně dobře jako kterýkoli jiný, ale nikdo za ním nestojí, takže ho každý prohlížeč odmítne s NET::ERR_CERT_AUTHORITY_INVALID. U veřejného webu se na straně prohlížeče nedá nic opravit: pořiď si skutečný certifikát. Let's Encrypt je zdarma a dá se plně zautomatizovat, takže self-signed certifikát na veřejné doméně v roce 2026 je konfigurace, která měla být dočasná.
Kde je uvidíš oprávněně:
- Čerstvá instalace. Spousta ovládacích panelů, routerů, NAS, Proxmox a výchozích balíčků webových serverů při instalaci vygeneruje self-signed certifikát, aby HTTPS vůbec fungovalo. Varování je očekávané, dokud nenainstaluješ řádný certifikát.
- Lokální vývoj. Vygenerovat ho jde jedním příkazem:
$ openssl req -x509 -newkey rsa:2048 -nodes -days 365 \ -keyout localhost.key -out localhost.crt \ -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"Řádek -addext je část, kterou lidé vynechávají, a bez něj Chrome certifikát odmítne jak kvůli jménu, tak kvůli důvěře. Pro cokoli víc než rychlý test je jednodušší mkcert: vytvoří malou lokální autoritu, nainstaluje ji do úložišť důvěryhodných certifikátů systému a prohlížeče a vystaví certifikáty, kterým tvůj vlastní počítač věří bez varování - a žádný jiný počítač ne.
- Interní služby s privátní CA. Firmy provozují vlastní autoritu pro interní jména. Tyto certifikáty ukazují v
s_clientna každém stroji, kam nebyl nainstalován firemní kořen,19 (self-signed certificate in certificate chain). Oprava je rozdistribuovat kořen, ne vypnout ověřování.
Dvě varování ohledně zkratek. Klikání na „Proceed“ učí uživatele klikat na „Proceed“, což je přesně návyk, který útočník potřebuje. A curl -k, verify=False v Python requests nebo NODE_TLS_REJECT_UNAUTHORIZED=0 vypínají kontrolu úplně - když zůstanou v produkčním kódu, znamenají, že kterýkoli stroj na cestě může provoz číst a měnit. Namiř klienta raději na správný soubor CA: curl --cacert, REQUESTS_CA_BUNDLE nebo NODE_EXTRA_CA_CERTS.
ERR_SSL_PROTOCOL_ERROR: handshake nikdy nedoběhl#
Každá chyba výše znamená, že certifikát dorazil a prohlížeči se nelíbil. ERR_SSL_PROTOCOL_ERROR obvykle znamená, že nedorazil žádný certifikát, protože to, co na portu odpovědělo, nemluvilo TLS tak, jak prohlížeč čekal.
Obyčejné HTTP na portu pro HTTPS. Zdaleka nejčastější příčina. Blok serveru má listen 443; bez ssl, aplikace poslouchá přímo na 443 bez nastaveného TLS, nebo jsi napsal https:// před port, který umí jen HTTP, třeba vlastní :3000 nebo :8080 nějaké aplikace. Verze téže chyby ve Firefoxu, SSL_ERROR_RX_RECORD_TOO_LONG, to říká poctivěji: dostal HTTP odpověď a snažil se ji přečíst jako TLS. s_client v chybových řádcích hlásí wrong version number nebo packet length too long. Zkontroluj direktivu listen:
listen 443 ssl;http2 on;http2 on; je zápis od nginx 1.25.1; starší verze píšou listen 443 ssl http2;. Tak či tak tam ssl musí být.
Pro to jméno není nastavený žádný certifikát. Některé servery, když dostanou jméno SNI, pro které nemají certifikát a nemají žádný výchozí, handshake raději přeruší, než aby poslaly něco špatného. Prohlížeč pak hlásí protokolovou chybu. Oprava je stejná jako u neshody jména: zajisti, aby jméno v konfiguraci existovalo.
Jen staré verze protokolu. Současné prohlížeče opustily TLS 1.0 a 1.1. Server, zařízení nebo starý vestavěný přístroj, který umí jen tyto, způsobí spíš ERR_SSL_VERSION_OR_CIPHER_MISMATCH než protokolovou chybu, ale test je stejný:
$ openssl s_client -connect example.com:443 -servername example.com -tls1_2 </dev/null$ openssl s_client -connect example.com:443 -servername example.com -tls1_3 </dev/nullPokud selžou oba, server je potřeba aktualizovat a v nginx je řádek, který nastavit, ssl_protocols TLSv1.2 TLSv1.3;.
Něco uprostřed. Antivirový software se „skenováním HTTPS“, firemní inspekční proxy a captive portály na wifi v hotelech a ve vlacích všechno zachytávají TLS. Podle toho, jak dobře to dělají, je výsledkem protokolová chyba nebo chyba autority a vydavatelem certifikátu, pokud ho vidíš, je dodavatel antiviru nebo firma, ne veřejná autorita. Pokud problém má jen jedna síť nebo jeden počítač, je to první podezřelý. Samotná stránka s tím nic nenadělá.
Před serverem je CDN a origin je špatně. Za Cloudflare se rozbitý origin projeví vlastními chybovými stránkami CDN: 525 znamená, že handshake mezi CDN a tvým serverem selhal, 526 znamená, že režim „Full (strict)“ shledal certifikát tvého originu neplatným. Režim „Flexible“ v kombinaci s originem, který přesměrovává na HTTPS, dává místo toho ERR_TOO_MANY_REDIRECTS. Který režim patří ke kterému originu, popisuje Cloudflare pro weby a herní servery.
Platný certifikát a přesto „Not secure“: mixed content#
Když je certifikát v pořádku, ale zámek chybí, je přeškrtnutý nebo nahrazený nápisem „Not secure“, stránka načtená přes HTTPS si něco stahuje přes obyčejné HTTP. Skripty, styly a iframy požadované přes http:// se rovnou blokují, což se obvykle projeví stránkou s chybějícím stylem nebo rozbitým menu, ne zjevnou chybou. Obrázky a média současný Chrome automaticky povyšuje na HTTPS, nebo je zablokuje, když povýšení selže.
Konzole prohlížeče jmenuje každou problematickou URL, takže z hledání se stane seznam. V Chrome vypadá takto:
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS,but requested an insecure script 'http://example.com/js/menu.js'.This request has been blocked; the content must be served over HTTPS.Oprava je přestat generovat URL s http:// - obvykle jde o staré absolutní URL uložené v databázi CMS, napevno zadaného hostitele souborů v šabloně, nebo o aplikaci za reverzní proxy, která si neuvědomuje, že návštěvník přišel přes HTTPS, a podle toho staví odkazy. To poslední vyžaduje, aby aplikace důvěřovala X-Forwarded-Proto od proxy. Úklid i nastavení proxy popisují HTTPS a Let's Encrypt jednoduše i namiř doménu na server a přidej SSL a bezpečné hromadné nahrazení v databázi popisuje přesun WordPressu na nový hosting.
Kde se o certifikát postará někdo za tebe#
Většina výše popsaných selhání se týká člověka, který spravuje soubory: obnova, která se rozbila, reload, který nikdo nespustil, cert.pem tam, kde měl být fullchain.pem. Na tarifech webhostingu a hostingu aplikací RE:NODE tuhle část řeší proxy slot. Namiř záznam A na zobrazenou adresu a certifikát se vystaví a obnovuje automaticky, v rámci 21denního okna před vypršením. Tvá aplikace dostává obyčejné HTTP na svém portu a skutečnou adresu návštěvníka v X-Forwarded-For, takže certifikát nikdy nedrží a není co skládat do řetězce.
Co se nemění: DNS pořád žije u poskytovatele tvé domény, takže špatný nebo zastaralý záznam A pořád způsobí problém se jménem nebo s vystavením a mixed content musí opravit aplikace. Když něco vypadá špatně, příkazy s_client výše fungují proti jakémukoli hostiteli, našeho nevyjímaje.
FAQ#
Je bezpečné kliknout na „Proceed anyway“?
Na vlastním serveru, abys potvrdil self-signed testovací certifikát, ano. Na cizím webu ne: varování znamená, že prohlížeč nedokázal potvrdit, s kým mluví, a nejčastější neškodné příčiny vypadají přesně jako odposlech. Po proklikání nikdy nezadávej heslo ani číslo karty.
Proč web funguje v Chrome, ale ne v curl nebo v mé aplikaci?
Server posílá certifikát bez intermediate. Desktopové prohlížeče si mezeru doplní samy, většina ostatních klientů ne. Servíruj soubor s celým řetězcem a rozdíl zmizí.
Proč chybu certifikátu vidí jen jeden návštěvník?
Protože problém je na jeho straně - špatné systémové hodiny, skenování HTTPS v antiviru, firemní proxy nebo přihlašovací stránka veřejné wifi. Zeptej se ho na přesný kód chyby a vydavatele uvedeného v detailech certifikátu.
Jak zjistím, kdy můj certifikát vyprší?
Spusť s_client přesměrovaný do openssl x509 -noout -dates z místa mimo tvou síť, jak je ukázáno výše. notAfter je konec platnosti. U čehokoli důležitého nech monitoring upozornit, když zbývá méně než 14 dní, aby se rozbitá obnova objevila dřív, než si jí všimnou návštěvníci.
Mohu u webu, na kterém pracuju, smazat chybu HSTS?
V Chrome má chrome://net-internals/#hsts pole „Delete domain security policies“, které z tvého prohlížeče odstraní záznam HSTS pro daný web. Nedokáže odstranit doménu z vestavěného preload seznamu prohlížeče a skutečná oprava je pořád platný certifikát.
Způsobuje bezplatný certifikát víc chyb než placený?
Ne. Prohlížeče věří Let's Encrypt stejně jako jakékoli komerční autoritě a chyby z tohoto článku se u placených certifikátů dějí stejně často - u chybějících řetězců dokonce častěji, protože soubor bundle přichází zvlášť.


