Fix Fly.io www Subdomain SSL Errors with Cloudflare (Error 525)

Fred· AI Engineer & Developer Educator8 min read

Deployed your app to Fly.io and the root domain works fine, but www.yoursite.com throws an SSL handshake error? You're not alone. This Error 525 happens because Fly.io requires separate SSL certificates for each hostname—and most deployment guides completely skip this step.

Here's the fix, tested and verified on a production deployment.

The Problem

You've deployed to Fly.io, added your custom domain, and everything looks good. Then you try visiting www.yoursite.com and get:

Error 525: SSL handshake failed

Meanwhile, yoursite.com (without www) works fine. What gives?

Why This Happens

Fly.io uses Let's Encrypt to provision SSL certificates, but here's the catch: adding a certificate for example.com does NOT automatically cover www.example.com.

They're treated as completely separate hostnames. You need to explicitly add a certificate for the www subdomain.

The Solution (5 Steps)

Step 1: Install Fly.io CLI

If you haven't already, install flyctl:

# Install flyctl
curl -L https://fly.io/install.sh | sh

# Add to PATH
export FLYCTL_INSTALL="$HOME/.fly"
export PATH="$FLYCTL_INSTALL/bin:$PATH"

# Make it permanent (add to ~/.bashrc or ~/.zshrc)
echo 'export FLYCTL_INSTALL="$HOME/.fly"' >> ~/.bashrc
echo 'export PATH="$FLYCTL_INSTALL/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

# Verify it works
flyctl version

Step 2: Check Your Current Certificates

First, see what certificates you already have:

flyctl certs list -a YOUR_APP_NAME

You'll probably see something like:

Host Name                 Added                Status
example.com              1 month ago          Ready
api.example.com          1 month ago          Ready

Notice what's missing? The www subdomain.

Step 3: Add the www Certificate (This Fixes It)

Here's the critical command that solves the problem:

flyctl certs add www.example.com -a YOUR_APP_NAME

Fly.io will automatically provision a Let's Encrypt SSL certificate for your www subdomain. You'll see output like:

You are creating a certificate for www.example.com
We are using Let's Encrypt for this certificate.

Your certificate for www.example.com is being issued.
You can validate your ownership of www.example.com by:

1: Adding an AAAA record to your DNS service which reads:
    AAAA @ 2a09:8280:1::X:XXXX

Don't worry about the DNS validation—if your root domain is already working, the www subdomain will validate automatically.

Step 4: Verify the Certificate Is Ready

Wait 1-2 minutes, then check the certificate status:

flyctl certs show www.example.com -a YOUR_APP_NAME

Look for "Status: Ready". If it says "Awaiting certificates", wait another minute and check again.

Once ready, your certificate list should show:

flyctl certs list -a YOUR_APP_NAME
Host Name                 Added                Status
example.com              1 month ago          Ready
www.example.com          13 seconds ago       Ready

Perfect.

Step 5: Configure Cloudflare SSL/TLS Mode

This step is critical if you're using Cloudflare for DNS (which you should be).

  1. Log in to your Cloudflare dashboard
  2. Select your domain
  3. Go to SSL/TLSOverview
  4. Set the encryption mode to "Full" (not "Flexible" or "Full (strict)")

Why "Full"?

  • Flexible: Cloudflare uses HTTPS to your visitors, but HTTP to Fly.io (insecure, causes issues)
  • Full: Cloudflare uses HTTPS to both visitors and Fly.io (correct)
  • Full (strict): Requires a trusted certificate authority, but Fly.io manages its own certs (causes errors)

Configure Your DNS Records (Cloudflare)

Make sure your DNS records are set up correctly:

Root Domain (example.com):

  • Type: A or AAAA
  • Name: @ (or leave blank)
  • Content: Your Fly.io IP address (from flyctl ips list)
  • Proxy status: Proxied (orange cloud enabled)

www Subdomain (www.example.com):

  • Type: CNAME
  • Name: www
  • Target: example.com (points to root domain)
  • Proxy status: Proxied (orange cloud enabled)

The orange cloud (Proxied) is important—it routes traffic through Cloudflare's CDN and enables SSL.

Test Everything

Verify both domains work:

# Test root domain
curl -I https://example.com

# Test www subdomain
curl -I https://www.example.com

Both should return HTTP/2 200 (or HTTP/1.1 200). If you get errors, see the troubleshooting section below.

Common Errors and Fixes

Error 525: SSL Handshake Failed

Symptoms: www subdomain throws Error 525, root domain works fine

Causes:

  1. Missing www certificate on Fly.io
  2. Cloudflare SSL/TLS mode is set to "Flexible" or "Full (strict)"

Fix:

# Add the www certificate
flyctl certs add www.example.com -a YOUR_APP_NAME

# Wait 2 minutes for certificate issuance
sleep 120

# Verify it's ready
flyctl certs show www.example.com -a YOUR_APP_NAME

Also verify Cloudflare SSL/TLS mode is set to "Full".

www Subdomain Returns Connection Timeout

Symptoms: www subdomain doesn't load at all, no error page

Causes:

  1. DNS CNAME record missing or incorrect
  2. DNS not proxied through Cloudflare
  3. DNS propagation incomplete

Fix:

  1. Check your Cloudflare DNS settings for the www CNAME record
  2. Ensure the orange cloud (Proxied) is enabled
  3. Wait 5-15 minutes for DNS propagation
  4. Clear your browser cache or test in incognito mode

Certificate Shows "Awaiting Certificates" for More Than 5 Minutes

Symptoms: flyctl certs show keeps saying "Awaiting certificates"

Causes:

  1. DNS records not pointing to Fly.io correctly
  2. Cloudflare proxy interfering with certificate validation

Fix:

# Verify your DNS records are correct
dig www.example.com

# Check if DNS is resolving to Fly.io
nslookup www.example.com

# If stuck, delete and re-add the certificate
flyctl certs delete www.example.com -a YOUR_APP_NAME
flyctl certs add www.example.com -a YOUR_APP_NAME

Root Domain Works, www Redirects to Root (But You Want Both)

Symptoms: Visiting www.example.com redirects to example.com

Causes: This might be intentional behavior in your app code

Fix: If you want both to work independently, ensure:

  1. Both certificates exist in Fly.io
  2. Your app doesn't have redirect logic forcing www → non-www
  3. Both DNS records are configured correctly

Why Separate Certificates Matter

In traditional shared hosting, a wildcard SSL certificate (*.example.com) covers all subdomains. But Fly.io provisions certificates individually through Let's Encrypt.

This gives you more control but requires explicit setup for each subdomain:

  • example.com → needs its own certificate
  • www.example.com → needs its own certificate
  • api.example.com → needs its own certificate
  • blog.example.com → needs its own certificate

You get the idea.

Best Practices for Fly.io + Cloudflare

  1. Add certificates for all subdomains you plan to use before going live
  2. Use Cloudflare's "Full" SSL/TLS mode for Fly.io deployments
  3. Enable Cloudflare proxy (orange cloud) for CDN and DDoS protection
  4. Test both www and non-www versions before announcing your site
  5. Set up redirects in your app if you want to force one version over the other

Useful Commands for Troubleshooting

# List all certificates for your app
flyctl certs list -a YOUR_APP_NAME

# View detailed info for a specific certificate
flyctl certs show www.example.com -a YOUR_APP_NAME

# Delete a certificate (if you need to start over)
flyctl certs delete www.example.com -a YOUR_APP_NAME

# Check your app's IP addresses
flyctl ips list -a YOUR_APP_NAME

# Check DNS resolution
dig example.com
dig www.example.com

# Test SSL connection with verbose output
curl -vI https://www.example.com

# Check app status
flyctl status -a YOUR_APP_NAME

# View app logs (useful for debugging)
flyctl logs -a YOUR_APP_NAME

Complete Setup Checklist

Use this checklist to verify everything is configured correctly:

Fly.io Certificates:

  • Root domain certificate exists and shows "Ready"
  • www subdomain certificate exists and shows "Ready"
  • Any other subdomains have certificates added

Cloudflare DNS:

  • A or AAAA record for root domain pointing to Fly.io IP
  • CNAME record for www subdomain pointing to root domain
  • All records have orange cloud (Proxied) enabled

Cloudflare SSL/TLS:

  • Encryption mode set to "Full" (not Flexible or Full strict)
  • Edge Certificates shows valid SSL

Testing:

  • https://example.com returns 200 OK
  • https://www.example.com returns 200 OK
  • No SSL warnings in browser
  • Both domains show secure lock icon

Real-World Example

Here's what the complete setup looks like for ftashark.com:

# Check certificates
$ flyctl certs list -a ftashark

Host Name                 Added                Status
ftashark.com              1 month ago          Ready
www.ftashark.com          13 seconds ago       Ready
api.ftashark.com          1 month ago          Ready

Cloudflare DNS:

Type    Name    Content                          Proxy
A       @       66.241.124.123                   Proxied
CNAME   www     ftashark.com                     Proxied
CNAME   api     ftashark.com                     Proxied

Cloudflare SSL/TLS: Full mode

Result: All three URLs work correctly with valid SSL.

Why Most Guides Skip This

Most Fly.io deployment tutorials focus on getting your app running and adding a single custom domain. They assume you'll only use example.com or www.example.com—not both.

But in reality, users type both versions. Search engines index both. And you don't want half your traffic hitting SSL errors.

Adding the www certificate takes 30 seconds but saves hours of debugging later.

Alternative: Redirect www to Non-www (Or Vice Versa)

If you don't want to maintain both versions, you can set up a redirect in your app.

Option 1: Redirect www → non-www

Most frameworks have middleware for this. For example, in Express.js:

app.use((req, res, next) => {
  if (req.hostname.startsWith('www.')) {
    return res.redirect(301, `https://${req.hostname.slice(4)}${req.url}`)
  }
  next()
})

Option 2: Redirect non-www → www

app.use((req, res, next) => {
  if (!req.hostname.startsWith('www.')) {
    return res.redirect(301, `https://www.${req.hostname}${req.url}`)
  }
  next()
})

But even if you redirect, you still need both certificates or the redirect won't work (users will hit the SSL error before your app can redirect them).

Conclusion

The Fly.io www subdomain SSL issue is one of those "gotchas" that trips up even experienced developers. The fix is simple once you know it:

  1. Add a separate certificate for www: flyctl certs add www.example.com -a YOUR_APP_NAME
  2. Set Cloudflare SSL/TLS to "Full" mode
  3. Wait 1-2 minutes for the certificate to be ready
  4. Test both domains

That's it. No complex configuration, no server restarts, no editing config files. Just one command that most deployment guides forget to mention.

Now your users can access your site with or without www, and both will work correctly.


Related guides:

Resources:

Fred

Fred

AUTHOR

Full-stack developer with 10+ years building production applications. I've been deploying to Cloudflare's edge network since Workers launched in 2017.