An "invalid SSL certificate" 526 error is one of the more specific failures a user can land on. It only appears when Cloudflare is in front of your site and your SSL/TLS encryption mode is set to either Full (strict) or Strict. The error means Cloudflare reached your origin server, opened a TLS connection successfully, and refused to trust the certificate the origin presented. The TLS handshake worked; the trust check did not.
The cert at the origin is one of: expired, self-signed, signed by an untrusted (or internal) CA, missing intermediate certificates in the chain, or issued for a hostname different from the one Cloudflare is connecting to. This guide walks through each cause with the exact openssl command to confirm it, then ends with the right fix — which, for almost every site, is to install a free Cloudflare Origin Certificate at the origin rather than to downgrade your encryption mode.
What 526 actually means
Cloudflare uses a custom set of 5xx status codes to distinguish failure modes that real HTTP doesn't separate. Knowing the right one saves an hour of chasing the wrong cause:
| Cloudflare error | What Cloudflare saw | Most common cause |
|---|---|---|
| 520 Web Server Returned an Unknown Error | Origin returned an empty / malformed / oversized response | App crash; oversized headers; firewall reset |
| 521 Web Server Is Down | TCP connection to origin actively refused | Origin process down; firewall blocks Cloudflare IPs |
| 522 Connection Timed Out | TCP connection to origin never completed | Network blocking; origin overloaded; firewall silently dropping |
| 523 Origin Is Unreachable | No route from Cloudflare to origin IP | DNS A record points at unreachable IP |
| 524 A Timeout Occurred | TCP + TLS succeeded; origin took > 100 s to send any response | Slow query / slow app — see 504 Gateway Timeout |
| 525 SSL Handshake Failed | TLS handshake itself failed (cipher mismatch, no cert) | Origin doesn't speak TLS on the port; cipher / protocol mismatch |
| 526 Invalid SSL Certificate | TLS handshake succeeded; cert chain failed validation | This article |
Mental model:
- 521 / 522 / 523 — Cloudflare can't get to your origin at all.
- 525 — Cloudflare connected, but the TLS handshake itself broke.
- 526 — Cloudflare connected, the handshake succeeded, but the certificate failed to validate against Cloudflare's trust store.
- 524 — Everything TLS-related worked; the origin just never replied.
If your error is 525, not 526, the diagnosis path is different (cipher / protocol). If it's 522 or 521, you have a connectivity problem, not a cert problem. Read the exact code on the Cloudflare error page before applying any fix from this article.
Cloudflare's SSL/TLS modes (and which one triggers 526)
Cloudflare offers five encryption modes between the visitor and the origin. The mode you're on determines whether you can ever see a 526:
| Mode | Visitor → Cloudflare | Cloudflare → Origin | Origin cert validation |
|---|---|---|---|
| Off | HTTP only | HTTP only | n/a |
| Flexible | HTTPS | HTTP | n/a (no TLS to origin) |
| Full | HTTPS | HTTPS | Cert is not validated — accepts self-signed |
| Full (strict) | HTTPS | HTTPS | Cert must be valid + trusted + match hostname |
| Strict (SSL-only origin pull) | HTTPS | HTTPS | Same as Full (strict), additionally requires Authenticated Origin Pulls |
You can only see 526 in Full (strict) or Strict mode. In Full, Cloudflare connects over HTTPS but skips trust validation — so even a self-signed cert works. In Flexible, the origin connection is plain HTTP, so there's no cert involved.
Where to check: Cloudflare dashboard → SSL/TLS → Overview. The current mode is highlighted on that page.
This is important because the most common "fix" advice for 526 is to drop from Full (strict) down to Full — which makes the error go away by no longer validating the cert at all. That's not a fix; it's hiding the problem at the cost of your origin's TLS guarantees. The right fix is to make the origin's cert valid; see the end of this article.
Step 1 — Confirm what cert your origin is actually serving
Don't trust the dashboard or the cert file you think you deployed — go look at what's actually on the wire. Use the origin's IP address (or its real hostname behind Cloudflare), not the public Cloudflare-fronted hostname:
# Get the origin IP from the Cloudflare DNS panel (the "grey-cloud" / proxied = OFF view)
# Then connect directly, sending SNI for the public hostname:
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
Replace <ORIGIN_IP> with the actual origin IP. Without -servername, your origin server (Nginx / Apache / a load balancer) may return a default cert that isn't the one Cloudflare sees in production traffic.
What you're looking for in the output:
notAfterin the past → expired (cause 1).issuer= same as subject → self-signed (cause 2).issuer= an internal CA your servers trust but Cloudflare doesn't → untrusted CA (cause 4).- SANs that don't include the public hostname Cloudflare is using → hostname mismatch (cause 3).
- No certificate returned, or only a leaf with no intermediates → incomplete chain (cause 4).
For the full openssl playbook, see How to check and verify SSL certificates with OpenSSL.
Cause 1 — Origin certificate expired
The most common cause, especially after weekend deploys. The cert at the origin past its notAfter date.
Confirm
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 2>/dev/null \
| openssl x509 -noout -dates
# notBefore=Feb 10 08:14:32 2025 GMT
# notAfter=May 11 08:14:31 2026 GMT ← if past today, this is your error
Or scriptable, exits non-zero if less than 7 days remain:
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 2>/dev/null \
| openssl x509 -checkend $((7*86400)) -noout
Fix
Renew the origin cert (or, better, switch the origin to a Cloudflare Origin Certificate — see "the right fix" below). Step-by-step renewal walkthrough: How to renew an SSL certificate (step-by-step).
After renewing, reload the web server so the new cert is served. A surprising number of 526s after a renewal are because the cert was on disk but Nginx/Apache was never reloaded.
Cause 2 — Self-signed cert in Full (strict) mode
You generated a cert with openssl req -x509 for testing, deployed it, set Cloudflare to Full (strict), and now every request returns 526.
Confirm
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 2>/dev/null \
| openssl x509 -noout -subject -issuer
# subject=CN = example.com
# issuer= CN = example.com ← same → self-signed
Fix
Two real options:
- Install a Cloudflare Origin Certificate (free, 15-year validity, trusted only by Cloudflare's edge — perfect for this exact use case). See "the right fix" below.
- Issue a real public cert via Let's Encrypt / certbot. The DNS-01 challenge works even when the public hostname is proxied by Cloudflare; HTTP-01 needs the origin to be reachable on port 80 directly, which is harder to arrange behind the Cloudflare proxy.
What you should not do: drop the Cloudflare mode from Full (strict) to Full. That makes the error go away by accepting your self-signed cert — and accepting any MITM cert someone could substitute.
Cause 3 — Hostname mismatch
The cert at the origin is valid and trusted, but its SANs don't include the public hostname Cloudflare is connecting under. Often happens when the origin serves multiple sites and SNI selects the wrong one — or when the origin cert was issued for the internal hostname only (web1.internal) and not the public name.
Confirm
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 2>/dev/null \
| openssl x509 -noout -ext subjectAltName
# DNS:web1.internal ← public hostname example.com is missing
Fix
Re-issue the cert with the public hostname in the SAN list. If you're using a Cloudflare Origin Certificate (recommended), add the public hostname when generating it in the Cloudflare dashboard. If you're using a public CA cert, generate a new CSR including the public hostname:
openssl req -new -newkey rsa:2048 -nodes \
-keyout origin.key -out origin.csr \
-subj "/CN=example.com" \
-addext "subjectAltName = DNS:example.com,DNS:www.example.com"
For deeper coverage of SAN-based mismatches (and why this is also the cause behind err_cert_common_name_invalid in browsers), see How to fix net::err_cert_common_name_invalid.
Cause 4 — Untrusted CA or incomplete chain
The origin's cert is signed by an internal CA, by an obscure CA Cloudflare doesn't include in its trust store, or by a real CA whose intermediate is not being served.
Cloudflare's edge maintains its own trust store. It includes major public CAs (Let's Encrypt, DigiCert, Sectigo, GlobalSign, Google Trust Services, ZeroSSL, etc.) plus Cloudflare Origin CA. It does not include your internal corporate CA.
Confirm
For an internal CA, the issuer line tells you immediately:
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 2>/dev/null \
| openssl x509 -noout -issuer
# issuer= O = Internal Corp CA, CN = Internal Root
For a missing-intermediate problem, the chain length tells you:
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 -showcerts 2>/dev/null \
| grep -c 'BEGIN CERTIFICATE'
# 1 ← only the leaf; intermediate is missing
# 2 or 3 ← good
Or run a full openssl verify against the system trust store:
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 -showcerts 2>/dev/null \
| awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/' > served.pem
openssl verify -untrusted served.pem served.pem
# example.com.crt: OK ← chain is complete
# unable to get local issuer certificate ← intermediate is missing
Fix
For an internal CA, you can't get Cloudflare to trust it. Switch to a Cloudflare Origin Certificate or a public-CA cert at the origin.
For a missing intermediate, build a fullchain.pem (leaf + intermediates concatenated) and serve that as ssl_certificate:
cat leaf.crt intermediate.crt > fullchain.pem
sudo cp fullchain.pem /etc/nginx/ssl/example.com.crt
sudo nginx -t && sudo systemctl reload nginx
Re-check chain length is now ≥ 2 with the grep -c 'BEGIN CERTIFICATE' command.
Cause 5 — Cloudflare reaching the wrong origin
Less common, but worth ruling out: your DNS A record (the orange-cloud / proxied entry) points at an IP that no longer hosts your site, and that IP belongs to a different service whose cert is for a different hostname.
Confirm
- In Cloudflare DNS, find the A record for
example.com. Note the IP. - Connect directly with
openssl s_client -servername example.com -connect <THAT_IP>:443and dump the cert. - If the cert is for someone else's hostname, the A record is stale.
Fix
Update the A record to point at your real origin. If the origin is now behind a different reverse proxy or a load balancer, point at that.
The right fix: install a Cloudflare Origin Certificate
For 90% of sites that are behind Cloudflare, the right answer to 526 is not to renew a public cert at the origin — it's to install a Cloudflare Origin Certificate. This is a free certificate Cloudflare issues for your domain that:
- Is valid for up to 15 years (no renewal toil).
- Is trusted by Cloudflare's edge only — useless to anyone bypassing Cloudflare.
- Supports wildcards out of the box.
- Works in Full (strict) mode without any extra trust configuration.
Generate one (Cloudflare dashboard)
- Cloudflare dashboard → SSL/TLS → Origin Server → Create Certificate.
- Hostnames: include the apex (
example.com) and any subdomains you serve from this origin (*.example.comis common). - Choose PEM format and an expiry of 15 years.
- Copy the Origin Certificate PEM and the Private Key to the origin server.
Install on Nginx
sudo mkdir -p /etc/nginx/ssl/cloudflare
sudo tee /etc/nginx/ssl/cloudflare/origin.crt <<'EOF'
-----BEGIN CERTIFICATE-----
... paste the Origin Certificate PEM here ...
-----END CERTIFICATE-----
EOF
sudo tee /etc/nginx/ssl/cloudflare/origin.key <<'EOF'
-----BEGIN PRIVATE KEY-----
... paste the Private Key PEM here ...
-----END PRIVATE KEY-----
EOF
sudo chmod 600 /etc/nginx/ssl/cloudflare/origin.key
# Update the server block
# ssl_certificate /etc/nginx/ssl/cloudflare/origin.crt;
# ssl_certificate_key /etc/nginx/ssl/cloudflare/origin.key;
sudo nginx -t && sudo systemctl reload nginx
Verify
echo | openssl s_client -servername example.com -connect <ORIGIN_IP>:443 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
# subject=O = Cloudflare, Inc., CN = example.com
# issuer= O = "Cloudflare, Inc.", CN = Origin ECC Issuing Certificate Authority
# notAfter=May 09 2041 ...
Now Cloudflare → origin handshakes succeed in Full (strict) mode and you've removed cert renewal from your operational burden for the next 15 years.
One caveat: the Origin Certificate is only trusted by Cloudflare. If you ever need to bypass Cloudflare (an internal monitor hitting the origin directly, an integration that talks to the origin without going through the Cloudflare edge), those clients will see an untrusted cert. For internal direct-to-origin monitoring, either point the monitor through Cloudflare too, install the Cloudflare Origin Root in the monitor's trust store, or run a separate public cert alongside the Origin Certificate (Nginx supports multiple
ssl_certificatedirectives).
What not to do
The internet is full of advice telling you to fix 526 by changing the Cloudflare SSL/TLS mode from Full (strict) to Full. Don't.
- Full mode tells Cloudflare to connect over HTTPS but skip cert validation entirely. Your data is still encrypted Cloudflare ↔ origin — but you've lost the guarantee that you're talking to your origin. Any party that can intercept the connection between Cloudflare and your origin can substitute their own cert and you'll never know. For sites with public traffic on the open internet, that downgrade is a meaningful security regression.
- Flexible is worse — it sends traffic to your origin over plain HTTP. Visitors see HTTPS in their browser; the connection from Cloudflare to your origin is plaintext. Don't.
The right fix is at the origin (renew, re-issue, or install an Origin Certificate), not at the Cloudflare mode setting. The mode setting only changes whether Cloudflare checks — the underlying cert problem remains.
Operational tips
- Always check from the origin IP, not the public hostname. When Cloudflare proxy is on (orange cloud),
openssl s_clientagainst the public hostname connects to Cloudflare's edge, not your origin — you'll see Cloudflare's edge cert, not the origin cert that's actually causing 526. Connect to the origin IP directly with-servernamefor the public name. - Confirm the encryption mode in writing. A Cloudflare admin downgrading the mode "to fix it" is a common shadow-fix you only discover months later when someone re-enables Full (strict) for compliance. Document the current mode and pin it.
- Origin Certificate renewals are 15-year by default. That's not "set and forget" — it's "set and forget for 15 years, then it's an emergency". Calendar the expiry, document the renewal owner, and put it in your SSL monitoring (see below).
- Authenticated Origin Pulls is the next step up. Once Full (strict) is solid, enable Authenticated Origin Pulls so Cloudflare presents a client cert when connecting to your origin and your origin only accepts connections from Cloudflare. Closes the bypass-Cloudflare attack surface entirely.
- Cloudflare's SSL/TLS Recommender (in the dashboard) will automatically suggest the right mode for your origin — useful as a second opinion.
Catch invalid origin certs before users do
The painful version of error 526 is "Cloudflare → origin trust broke at 02:00 and 100% of traffic has been failing since". From the Cloudflare side it is a 526 logged in the analytics; from your side it is the support inbox waking you up. The right safety net is monitoring the origin cert directly — not just the public Cloudflare-fronted hostname.
What that looks like in practice:
- Configure SSL certificate monitoring to connect to the origin IP with the public hostname as SNI, the same way Cloudflare does. The check then validates the origin cert chain against a public trust store and alerts on expiry, untrusted issuer, hostname mismatch, or missing intermediate.
- Set up an HTTPS uptime check through Cloudflare as well — that catches anything that breaks specifically in the Cloudflare → origin hop.
- Alert on the first failure from any region, not the second consecutive failure. A 526 surfaces immediately to every visitor; the monitor should match.
Xitoring's SSL certificate monitoring supports both — origin-IP probes with custom SNI for the Cloudflare-behind case, plus public-hostname probes through Cloudflare. Pair with HTTPS uptime monitoring so you catch the difference between "the cert is bad" and "Cloudflare's edge has a separate problem". The other articles in this series cover the rest of the SSL operational picture: renewing certificates, verifying with openssl, and the related browser-side error err_cert_common_name_invalid.
Summary
Error 526 — invalid SSL certificate — fires when Cloudflare is in Full (strict) or Strict mode and the origin's cert fails validation. To fix it:
- Confirm you're seeing 526, not 524 / 525 / 521. Each Cloudflare 5xx maps to a different cause; the wrong fix wastes hours.
- Connect to the origin IP directly with
openssl s_client -servername. Look at subject, issuer, dates, SANs, chain length. - Match the failure to one of the five causes: expired (1), self-signed (2), hostname mismatch (3), untrusted CA / incomplete chain (4), or wrong origin IP (5).
- Apply the right fix at the origin — re-issue, re-deploy, build a full chain — not "downgrade Cloudflare to Full". Downgrading hides the failure but loses the trust guarantee.
- For most sites, install a Cloudflare Origin Certificate. Free, 15-year, trusted by Cloudflare's edge, eliminates renewal toil. Treat the 15-year expiry as a real renewal date, not a "set and forget".
- Monitor the origin cert directly (origin IP + public-hostname SNI) so a silent failure is caught before users see 526.
A 526 is a precise signal — there's a single layer that's broken, with a single set of well-understood causes. The discipline that pays off is checking the origin cert from the origin IP rather than from the public hostname, because every other check sees Cloudflare's edge cert and tells you nothing useful about the actual problem. Once that habit is in place, most 526s go from "30-minute mystery" to "two-minute fix".