A wordpress too many redirects https loop right after switching to Cloudflare’s free SSL is almost always the same bug: Cloudflare is in Flexible SSL mode, serving visitors HTTPS while fetching your origin over HTTP. WordPress sees the plain-HTTP request, notices the Site URL is https://, and redirects back to HTTPS — which Cloudflare forwards as HTTP, and the loop is on. Add two lines to wp-config.php to make WordPress respect Cloudflare’s terminated-TLS signal and the loop stops.
Last verified: 2026-04-23 on WordPress 6.5 with Cloudflare SSL and on a generic nginx reverse proxy. Originally published 2023-02-07, rewritten and updated 2026-04-23.
TL;DR
// wp-config.php — above the "stop editing" line
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
Why the loop starts
Cloudflare’s Flexible SSL mode sits in front of your origin and does TLS termination:
- Visitor →
https://example.com→ Cloudflare. - Cloudflare →
http://example.com→ your server. - WordPress sees plain HTTP. Your Site URL is
https://example.com. - WordPress emits a
301redirect to the HTTPS version to match its configured URL. - Browser follows the redirect → back to step 1.
Browsers cap redirects at about 20 hops and show ERR_TOO_MANY_REDIRECTS. The site is unreachable until you break the loop.
Quick fix — tell WordPress to trust the forwarded-proto header
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
Cloudflare (and almost every other TLS terminator) sets the X-Forwarded-Proto header to https so the origin knows the visitor is on a secure connection. This snippet checks for that header and promotes $_SERVER['HTTPS'] to 'on' — now WordPress thinks the request is already HTTPS and skips the redirect.
Place the snippet above the /* That's all, stop editing! */ comment in wp-config.php. The assignment has to happen before WordPress loads its core, which is why it sits in config rather than in theme code.

Blunter alternative — unconditional HTTPS=on
$_SERVER['HTTPS'] = 'on';
Lies to WordPress regardless of the actual request scheme. Works when your origin is only reachable through Cloudflare (Cloudflare’s proxy IPs whitelisted at the firewall, direct origin IP blocked). If your origin accepts raw HTTP from anywhere, an attacker who hits the HTTP port would trigger WordPress to emit HTTPS URLs / secure-flagged cookies over an insecure connection — bad. Prefer the conditional form unless you’ve locked down direct origin access.
The proper long-term fix
Flexible SSL is a quick-and-dirty convenience. For anything you’d leave running for months:
- Install a real certificate on the origin — Let’s Encrypt via certbot (see the .htaccess exclude for ACME challenges) or Cloudflare’s free Origin Certificate.
- In Cloudflare → SSL/TLS → Overview, switch the mode from Flexible to Full (strict).
- WordPress now sees real HTTPS end-to-end. Remove the
wp-config.phpworkaround.
The visible difference is zero — but Full (strict) means your origin is actually protected instead of just looking protected to the visitor.
Frequently asked questions
Cloudflare’s Flexible SSL mode serves the visitor over HTTPS but talks to your origin over plain HTTP. WordPress sees an HTTP request and, seeing the Site URL set to https://, redirects to https://. Cloudflare terminates TLS again, forwards HTTP, WordPress redirects — infinite loop. The fix is telling WordPress to trust Cloudflare’s HTTPS signal rather than relying on the request scheme it actually sees.
$_SERVER['HTTPS'] = 'on' always safe? Only when you control the proxy chain. Setting HTTPS unconditionally makes WordPress behave as if every request is secure — fine behind a trusted Cloudflare / load balancer that terminates TLS, risky if your origin is directly reachable over plain HTTP (an attacker could hit the HTTP port and WordPress would still emit HTTPS URLs in cookies). The conditional form that checks HTTP_X_FORWARDED_PROTO is safer because it only lies about HTTPS when the proxy said so.
Switch Cloudflare’s SSL mode from Flexible to Full (strict), install a real certificate on the origin (Let’s Encrypt via certbot, or Cloudflare’s free Origin Certificate), and let WordPress see actual HTTPS end-to-end. The $_SERVER['HTTPS'] patch works but it’s a workaround; the proper fix eliminates the HTTP hop entirely.
Yes — any TLS terminator that forwards HTTP to the origin produces the same redirect loop. AWS Application Load Balancer, nginx reverse proxy, Vercel, and most managed hosts set the X-Forwarded-Proto: https header, so the conditional form below works unchanged. Check var_dump($_SERVER) to confirm which header your stack sets — some use HTTP_X_FORWARDED_SSL or a custom header instead.
wp-config.php does the fix go? Above the /* That's all, stop editing! Happy publishing. */ comment — and before the require_once ABSPATH . 'wp-settings.php'; line that follows. WordPress reads the file top-to-bottom, and your $_SERVER['HTTPS'] assignment has to run before WordPress checks the server scheme.
Related guides
- How to Exclude .well-known from Redirection for Let’s Encrypt in Laravel — the ACME challenge step for real origin certificates.
- How to Fix cURL Error 60 SSL Certificate Problem in Laravel — the sibling “SSL problem from the other direction.”
- How to Disable Revisions and Autosave in WordPress — another
wp-config.phptweak. - How to Login a User Programmatically in WordPress — cookie/session considerations for mixed-scheme setups.
References
Cloudflare SSL modes: developers.cloudflare.com/ssl/origin-configuration/ssl-modes. WordPress is_ssl() internals: developer.wordpress.org/reference/functions/is_ssl.