test.ssl.sh

SSL/TLS Test Lab: 20 broken and modern HTTPS test sites

Each subdomain of test.ssl.sh is broken (or unusually modern) in exactly one way. Open it in a browser to see the real error, or check it with ssl.sh to see what the checker should report.

Last verified

How to use the lab

Open a scenario in the browser or app you are testing and compare what you see with the expected result below. Use the matching error page to explain the problem to someone else.

To test the checker itself, run curl ssl.sh/<scenario>.test.ssl.sh. Tools and libraries can be tested against the same hosts: a client that connects to expired, self-signed or wrong-host without complaining is not validating certificates.

Certificate errors

expired.test.ssl.sh

Fails
Certificate
Let's Encrypt, 6-day, never renewed
Server setup

A Let's Encrypt 6-day certificate (shortlived profile) that was issued once and never renewed, so it expired 160 hours after issuance.

In browsers

Chrome and Edge show NET::ERR_CERT_DATE_INVALID; Firefox shows SEC_ERROR_EXPIRED_CERTIFICATE; Safari says the connection is not private.

curl ssl.sh/expired.test.ssl.sh

Fails with the expired finding: notAfter is in the past and days_left is negative.

See also

self-signed.test.ssl.sh

Fails
Certificate
Self-signed
Server setup

A self-signed certificate generated by sslsh testlab gen: the issuer is the certificate itself, no CA involved.

In browsers

Chrome and Edge show NET::ERR_CERT_AUTHORITY_INVALID; Firefox shows MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT.

curl ssl.sh/self-signed.test.ssl.sh

Fails with self_signed (the chain is not trusted). Plain curl reports curl: (60) SSL certificate problem: self-signed certificate.

See also

untrusted-root.test.ssl.sh

Fails
Certificate
Private lab CA
Server setup

Leaf → intermediate → a private root (“ssl.sh Test Lab Untrusted Root”) that is in no trust store and is never published. The server sends the leaf and the intermediate.

In browsers

Chrome and Edge show NET::ERR_CERT_AUTHORITY_INVALID; Firefox shows SEC_ERROR_UNKNOWN_ISSUER.

curl ssl.sh/untrusted-root.test.ssl.sh

Fails with untrusted_root. Plain curl reports curl: (60) SSL certificate problem: self-signed certificate in certificate chain or unable to get local issuer certificate.

See also

wrong-host.test.ssl.sh

Fails
Certificate
ssl.sh's own certificate
Server setup

Serves ssl.sh's own valid Let's Encrypt certificate, which only covers ssl.sh and www.ssl.sh, not wrong-host.test.ssl.sh.

In browsers

Chrome and Edge show NET::ERR_CERT_COMMON_NAME_INVALID; Firefox shows SSL_ERROR_BAD_CERT_DOMAIN.

curl ssl.sh/wrong-host.test.ssl.sh

Fails with hostname_mismatch; the SAN list shows ssl.sh and www.ssl.sh. Plain curl reports no alternative certificate subject name matches target host name.

See also

incomplete-chain.test.ssl.sh

Depends on client
Certificate
Let's Encrypt, 6-day
Server setup

A valid Let's Encrypt certificate, but the server sends only the leaf (cert.pem instead of fullchain.pem), so the intermediate is missing.

In browsers

Usually loads: Chrome, Edge and Safari fetch the missing intermediate through the AIA URL, and Firefox ships known intermediates. Older Android devices, apps and command-line tools fail.

curl ssl.sh/incomplete-chain.test.ssl.sh

Fails with incomplete_chain. curl, OpenSSL, Python and Node.js report unable to get local issuer certificate.

See also

revoked.test.ssl.sh

Depends on client
Certificate
Let's Encrypt, 90-day, revoked
Server setup

A Let's Encrypt 90-day certificate (classic profile, which carries a CRL URL) revoked right after issuance with reason keyCompromise. It is re-issued and revoked about every 80 days.

In browsers

Firefox (CRLite) shows SEC_ERROR_REVOKED_CERTIFICATE. Chrome and Edge show NET::ERR_CERT_REVOKED only once the serial reaches Chrome's CRLSet; until then the page loads. Safari depends on Apple's revocation service.

curl ssl.sh/revoked.test.ssl.sh

Reports revoked when the checker consults the CA's CRL. Plain curl and OpenSSL don't check revocation and connect normally.

See also

client-auth-eku.test.ssl.sh

Fails
Certificate
Private lab CA
Server setup

A certificate from the lab CA whose Extended Key Usage contains both serverAuth and clientAuth: the multi-purpose combination publicly trusted CAs phased out in 2025–2026.

In browsers

Without the lab root: NET::ERR_CERT_AUTHORITY_INVALID / SEC_ERROR_UNKNOWN_ISSUER. With the name-constrained lab root imported it loads: browsers don't reject the extra clientAuth EKU.

curl ssl.sh/client-auth-eku.test.ssl.sh

Shows the client_auth_eku finding (a public CA won't renew this certificate with the same EKUs) plus untrusted_root.

See also

Protocol versions and key exchange

tls-v1-0.test.ssl.sh

Fails
Certificate
Let's Encrypt, 6-day
Server setup

Only TLS 1.0 is enabled (ssl_protocols TLSv1 with @SECLEVEL=0, because OpenSSL 3 refuses TLS 1.0 at its default security level).

In browsers

Every current browser refuses: Chrome and Edge show ERR_SSL_VERSION_OR_CIPHER_MISMATCH, Firefox shows SSL_ERROR_UNSUPPORTED_VERSION.

curl ssl.sh/tls-v1-0.test.ssl.sh

The quick check fails with tls_handshake_failed; a full check should list TLS 1.0 as the only enabled protocol.

See also

tls-v1-1.test.ssl.sh

Fails
Certificate
Let's Encrypt, 6-day
Server setup

Only TLS 1.1 is enabled (ssl_protocols TLSv1.1 with @SECLEVEL=0).

In browsers

Same as TLS 1.0: ERR_SSL_VERSION_OR_CIPHER_MISMATCH in Chrome and Edge, SSL_ERROR_UNSUPPORTED_VERSION in Firefox.

curl ssl.sh/tls-v1-1.test.ssl.sh

The quick check fails with tls_handshake_failed; a full check should list TLS 1.1 as the only enabled protocol.

See also

tls-v1-2-only.test.ssl.sh

Loads
Certificate
Let's Encrypt, 6-day
Server setup

Only TLS 1.2 is enabled (ssl_protocols TLSv1.2).

In browsers

Loads normally in every current browser, negotiated as TLS 1.2.

curl ssl.sh/tls-v1-2-only.test.ssl.sh

Passes with tls_version TLSv1.2; a full check notes that TLS 1.3 is not offered (informational, not an error).

See also

tls-v1-3-only.test.ssl.sh

Loads
Certificate
Let's Encrypt, 6-day
Server setup

Only TLS 1.3 is enabled (ssl_protocols TLSv1.3).

In browsers

Loads in every current browser. Clients limited to TLS 1.2 (Android 9 and older, Windows 10's built-in TLS stack, old Java 8 builds) fail with a protocol error.

curl ssl.sh/tls-v1-3-only.test.ssl.sh

Passes with tls_version TLSv1.3; a full check shows TLS 1.2 disabled.

See also

pq-hybrid-only.test.ssl.sh

Depends on client
Certificate
Let's Encrypt, 6-day
Server setup

TLS 1.3 only, and the only key-exchange group is the post-quantum hybrid X25519MLKEM768 (ssl_ecdh_curve X25519MLKEM768; nginx must be linked against OpenSSL 3.5 or newer).

In browsers

Recent Chrome, Edge, Firefox and Safari 26 connect because they send an X25519MLKEM768 key share by default. Older browsers and most HTTP libraries fail the handshake (Chrome: ERR_SSL_VERSION_OR_CIPHER_MISMATCH).

curl ssl.sh/pq-hybrid-only.test.ssl.sh

Passes with key_exchange_group X25519MLKEM768 when the checker's OpenSSL is 3.5+; with an older OpenSSL it fails with tls_handshake_failed.

See also

HTTP security headers and page content

hsts.test.ssl.sh

Loads
Certificate
Let's Encrypt, 6-day
Server setup

Valid certificate plus Strict-Transport-Security: max-age=31536000; includeSubDomains.

In browsers

Loads normally. After the first visit the browser rewrites http:// to https:// on its own (Chrome DevTools shows a “307 Internal Redirect”), and certificate errors on this host can no longer be clicked through.

curl ssl.sh/hsts.test.ssl.sh

Passes; a full check reports HSTS with max-age 31536000.

See also

no-hsts.test.ssl.sh

Loads
Certificate
Let's Encrypt, 6-day
Server setup

Valid certificate, no HSTS header; plain HTTP is redirected to HTTPS with a 301 only.

In browsers

Loads normally and looks fine. The weakness is invisible: every first request over plain HTTP can be intercepted (SSL stripping).

curl ssl.sh/no-hsts.test.ssl.sh

Passes the certificate checks; a full check adds the no_hsts finding.

See also

mixed-content.test.ssl.sh

Depends on client
Certificate
Let's Encrypt, 6-day
Server setup

An HTTPS page that loads an image and a script over plain http://.

In browsers

Chrome and Edge block the script and upgrade the image to HTTPS; the DevTools console shows “Mixed Content” messages. Firefox also blocks the script. The page itself tells you whether the HTTP script ran.

curl ssl.sh/mixed-content.test.ssl.sh

Passes: the TLS setup is fine. Mixed content is a page problem, not a certificate problem; scan the page instead.

See also

Keys and certificate lifetimes

long-validity.test.ssl.sh

Fails
Certificate
Private lab CA
Server setup

A lab-CA certificate valid for 3,650 days. Publicly trusted certificates have been capped at 200 days since 15 March 2026 (CA/Browser Forum ballot SC-081), falling to 47 days in 2029.

In browsers

Without the lab root: NET::ERR_CERT_AUTHORITY_INVALID. With the lab root imported, Chrome and Firefox load it (lifetime caps only apply to public roots), while Safari and other Apple clients reject TLS certificates valid longer than 825 days even from user-installed roots.

curl ssl.sh/long-validity.test.ssl.sh

Fails with untrusted_root; validity_days is 3650, far above the 200-day public maximum.

See also

short-lived-6-days.test.ssl.sh

Loads
Certificate
Let's Encrypt, 6-day
Server setup

A Let's Encrypt shortlived certificate: valid for 160 hours (about 6.7 days) and renewed automatically every 2–3 days.

In browsers

Loads normally.

curl ssl.sh/short-lived-6-days.test.ssl.sh

Passes; validity_days is 7 (160 hours rounded up) and days_left is between 0 and 6. A short remaining lifetime is expected here, not a renewal failure.

See also

ecc-p256.test.ssl.sh

Loads
Certificate
Let's Encrypt, 6-day
Server setup

A Let's Encrypt certificate with an ECDSA P-256 key.

In browsers

Loads normally.

curl ssl.sh/ecc-p256.test.ssl.sh

Passes; key_type is “ECDSA P-256”.

See also

rsa-4096.test.ssl.sh

Loads
Certificate
Let's Encrypt, 6-day, RSA 4096
Server setup

A Let's Encrypt certificate with a 4096-bit RSA key.

In browsers

Loads normally; the handshake costs the server more CPU than ECDSA P-256 but the difference is not visible to visitors.

curl ssl.sh/rsa-4096.test.ssl.sh

Passes; key_type is “RSA 4096”.

See also

Client certificates

mtls-required.test.ssl.sh

Fails
Certificate
Let's Encrypt, 6-day
Server setup

The server requires a client certificate issued by the lab client CA (ssl_verify_client on). A test client certificate is published at test.ssl.sh/downloads/.

In browsers

Without a client certificate nginx answers “400 Bad Request — No required SSL certificate was sent”. Import client.p12 (password ssl.sh) and the browser asks you to pick it; the page then loads.

curl ssl.sh/mtls-required.test.ssl.sh

The server certificate passes. An HTTP request without a client certificate gets 400; curl --cert client.pem --key client.key https://mtls-required.test.ssl.sh/ gets 200.

See also

Downloads

These files live at https://test.ssl.sh/downloads/. They are public on purpose; never use them outside testing.

Scenarios we do not run

Some classic test cases cannot be served by a current OpenSSL 3 build, or would only reproduce an error another scenario already shows.

RC4

RFC 7465 has prohibited RC4 in TLS since 2015 and every browser removed it in 2016. OpenSSL 3 keeps RC4 only in the legacy provider, so a stock nginx cannot offer it, and the browser result would be the same cipher-mismatch error as tls-v1-0.

RSA-1024

Public CAs have not issued 1024-bit RSA certificates for over a decade, and OpenSSL 3 at the security level most distributions ship refuses to load such a key. A private-CA version would only show the untrusted-issuer error, which untrusted-root already covers.

SSLv3 / SSLv2

Browsers removed SSLv3 after POODLE (2014), and OpenSSL 1.1.0 and later no longer ship SSLv2 at all; distribution builds also disable SSLv3.

SHA-1 signatures

Browsers stopped trusting SHA-1 certificates in 2017 and OpenSSL 3 rejects SHA-1 signatures at its default security level; a private-CA version would again only show the untrusted-issuer error.

How the lab is built

Every scenario runs on one nginx server. Certificate scenarios differ only in the certificate they send. TLS versions, key-exchange groups and client-certificate requests are settled before nginx knows which hostname was requested, so those scenarios each get their own listener and an SNI router on port 443 (ssl_preread) forwards connections to it.

Publicly trusted scenarios use Let's Encrypt 6-day certificates, so they fail only for the reason they are meant to show. Private certificates come from sslsh testlab gen.

The ssl.sh HSTS header deliberately omits includeSubDomains, so visiting ssl.sh does not affect the *.test.ssl.sh scenarios: certificate-error scenarios still offer a “proceed anyway” button, and HTTP requests in no-hsts and mixed-content are not upgraded because of the main site's HSTS.