Enviera

REPLIT + CLOUDFLARE

Error 525: SSL handshake failed on a Replit custom domain behind Cloudflare

What the 525 error means

Cloudflare shows Error 525: SSL handshake failed when two things are true: the TLS handshake between Cloudflare and your origin failed, and your zone's SSL/TLS mode is Full or Full (strict). [1]

With a Replit app, the origin is Replit's server for your custom domain. So a 525 means Cloudflare connected to Replit over HTTPS for that hostname and the certificate exchange never completed. It is a different error from 526: invalid SSL certificate, which Cloudflare shows in Full (strict) mode when it did get a certificate but could not validate it (expired, self-signed, wrong hostname, incomplete chain). [2]

That difference is your first clue. Cloudflare's list of 525 causes starts with “No valid SSL certificate is installed” at the origin. [1] On Replit you can't touch the server's ports or ciphers, but you do control which hostnames Replit has a certificate for. Start there.

Before you change anything: Replit's own position on the proxy

Replit's custom-domain docs say plainly that Cloudflare proxied records are a known problem: “Replit cannot automatically renew security certificates for that type”, and “Proxy mode (the orange cloud) interferes with SSL provisioning”. Their advice is to set the A record to DNS only (grey cloud). [3]

So the setup Replit supports is grey cloud. If you keep the orange cloud on, you are outside that setup, and the steps below help you find out what broke. They don't make the proxy a supported configuration.

Step 1: Find out which hostname returns 525

Open both https://yourdomain.com and https://www.yourdomain.com.

  • Only www fails, the apex works (or the other way round): go to Step 3a. This is the case reported in the Replit forum thread. [4]
  • Both fail: go to Step 2, then Step 3b.

Step 2: Test Replit directly, skipping Cloudflare

While the proxy is on, public DNS points at Cloudflare, so a normal browser or certificate check only ever sees Cloudflare's certificate. To see Replit's, take the A record value Replit gave you in the Domains tool (the IP your record points to). Cloudflare's docs show how to ask the origin for its certificate while bypassing Cloudflare with curl --connect-to: [5]

curl -svo /dev/null https://www.yourdomain.com --connect-to ::<REPLIT_A_RECORD_IP> 2>&1 | egrep -v "^{.*$|^}.*$|^* http.*$"

Run it once for the apex and once for www. Read the result:

  • The handshake fails and no certificate is printed: Replit has no certificate for that hostname. Go to Step 3a or 3b.
  • A certificate is printed but it has expired: that is the expired-certificate case. Follow /fix-replit-ssl.
  • A valid certificate for the right name is printed: Replit's side is fine for that name. Recheck the Cloudflare DNS record (Step 3b) and Cloudflare's other listed causes for 525 (port 443, SNI, cipher suites). [1]

The free checker below can't read Replit's certificate through the proxy. For a proxied hostname it tells you whether the name returns 525 or 526, whether the proxy is on, whether the replit-verify TXT record exists, whether there are AAAA records, and whether www points anywhere. Once the record is DNS only, it shows the certificate itself.

Step 3a: 525 on www only (www was never added in Replit)

Replit's docs: “www is a subdomain. Linking hat-tip.cc does not automatically serve www.hat-tip.cc.” You have to add www “as a separate entry in the Domains tool”. [3]

When Cloudflare forwards a www request to Replit and Replit has no certificate covering www, the handshake fails and Cloudflare reports 525. The forum thread describes exactly this: apex fine, www 525. [4]

Two fixes. Pick one:

  1. Serve www from Replit: add www.yourdomain.com as its own domain in Replit's Domains tool, add the A and TXT records Replit shows for it, set the new A record to DNS only (grey cloud), since proxy mode interferes with SSL provisioning, and wait for it to verify. [3]
  2. Don't send www to Replit at all: create a Cloudflare redirect rule that sends https://www.* to https://${1} with a 301 and the query string preserved (Cloudflare's “Redirect from WWW to root” example). [6] The forum author keeps the www record proxied so the rule can catch the request before it reaches Replit. [4] We recommend this over hosting a domain on Replit only to redirect it.

Step 3b: 525 on the apex (or on both)

Replit issues and renews your certificate through DNS validation, and lists what blocks it. [3] Check each:

  1. Proxy status. The record pointing at Replit is proxied (orange cloud). Replit says this interferes with provisioning and blocks automatic renewal. Set it to DNS only. [3]
  2. The replit-verify TXT record. It “must stay in your DNS for the full lifetime of the domain”. A missing or changed TXT record “blocks verification and certificate renewal”. [3]
  3. One A record. Keep only one A record at the root, pointing to Replit. [3]
  4. No AAAA records on the same hostname. Replit only supports A records, and its docs say AAAA records there can block SSL. [3]
  5. Domain status in Replit. If the domain still shows as pending or verifying, the certificate hasn't been issued yet. Replit says DNS changes can take time to be seen; use a DNS lookup tool to confirm the records are visible before changing anything else. [3]

If renewal already failed while the record was proxied, one user in the same forum thread reports that the fix was to “un-proxy the domain in cloudflare, delete the domain from Replit and re configure it”, and that Replit support told them renewal doesn't work while proxied. [4]

After the fix, run Step 2 again. A certificate for the right name should come back.

Mode changes that look like fixes

“Switch from Full (strict) to Full.” The forum thread recommends this, saying Full (strict) only trusts certificates from Cloudflare's Origin CA, so it rejects Replit's Let's Encrypt certificate. [4] Cloudflare's docs say otherwise: Full (strict) accepts a certificate from “a publicly trusted certificate authority” or Cloudflare's Origin CA, as long as it is unexpired and matches the hostname. [7] And if strict validation were the problem, you would see 526, not 525. [2]

Full mode has a cost. Cloudflare says that in Full mode the origin certificate is “not validated in any way. It can be expired, self-signed, or not even have a matching CN/SAN entry”. [8] That is how an expired certificate can sit behind Cloudflare unnoticed (see below).

“Switch to Flexible.” Flexible makes Cloudflare talk to your origin over plain HTTP. Cloudflare warns it causes a redirect loop if the origin redirects HTTP to HTTPS, and recommends Full or Full (strict) for logins and personal data. [9] It hides the 525 by skipping the handshake. It doesn't fix the missing certificate. The user report in the forum thread says the same: “Activating Flexible SSL didn't solve the issue as the Origin SSL was still not solved.” [4]

“Install a Cloudflare Origin CA certificate on Replit.” Replit issues and renews the certificate for your custom domain itself, through DNS validation. [3] The forum author adds that there is nowhere on Replit to upload one. [4]

What we saw on a real site

On a US marketplace site built on Replit with DNS on Cloudflare in Full mode, www served no certificate at all (ERR_CONNECTION_CLOSED in the browser, SSL_ERROR_SYSCALL from curl), and the apex's Let's Encrypt certificate had expired days earlier. Because Full mode accepts an expired origin certificate, Cloudflare kept serving the apex and nobody saw the expiry. We fixed it by disconnecting and reconnecting the domain in Replit's Domains tab, and the reconnect issued new certificates for both names. The full walkthrough of that fix is on /fix-replit-ssl.

How to stop it happening again

From the same incident:

  1. Monitor certificate expiry at the origin, not through Cloudflare, and alert when fewer than 20 days are left.
  2. Don't host the apex on Replit only to redirect it; use a Cloudflare redirect rule.
  3. Use DNS only (grey cloud) records and keep a permanent replit-verify TXT on every hostname Replit serves.
  4. Put a calendar check halfway through each certificate's lifetime.

Check your domain with the free custom-domain certificate checker

Enter your domain and it checks the apex and www: whether each returns a Cloudflare 525 or 526, whether the proxy is on, whether the replit-verify TXT exists, any AAAA records, and whether www is pointed. Once a record is DNS only (grey cloud), it also shows which certificate that name presents and how many days are left. Behind the proxy, use the curl command in Step 2 to see Replit's certificate.

Check my domain

Still stuck, or the app has more than a domain problem? Send it to us for a Rescue Audit.

Sources

  1. Cloudflare, "Error 525: SSL handshake failed"
  2. Cloudflare, "Error 526: invalid SSL certificate"
  3. Replit Docs, "Custom domains"
  4. Replit Community Forum, "Updated Cloudflare Integrations" (user posts, 2026-07-05 and 2026-09-02, not Replit staff)
  5. Cloudflare, "Gathering information for troubleshooting sites"
  6. Cloudflare, "Redirect from WWW to root"
  7. Cloudflare, "Full (strict)"
  8. Cloudflare, "Full"
  9. Cloudflare, "Flexible"