After moving WordPress behind Cloudflare, an Nginx proxy, a load balancer, or another reverse proxy, your site may start redirecting HTTP to HTTPS indefinitely. In a browser, this often appears as a “too many redirects” error, even though the SSL certificate is correctly installed on the proxy.
The typical cause is that different layers have a different view of the protocol. The user connects to the proxy over HTTPS, but the proxy connects to the PHP server over regular HTTP. WordPress sees HTTP and tries to redirect the request to HTTPS, while the proxy sends it to the backend as HTTP again. Below is a sequence of checks and a safe way to fix the issue.
Why a Redirect Loop Occurs
A reverse proxy setup includes at least two separate connections: the browser to the proxy and the proxy to the WordPress server. HTTPS may terminate at Cloudflare, Nginx, or a load balancer, while HTTP is used between the proxy and PHP-FPM.
PHP usually determines whether a connection is secure through the `$_SERVER['HTTPS']` variable or the WordPress `is_ssl()` function. If the proxy does not pass information about the original protocol, the backend treats the request as HTTP. WordPress, configured with an `https://example.com` address, responds with a 301 or 302 redirect to HTTPS. The next request follows the same route and is detected as HTTP again.
It is important to distinguish this issue from DNS errors, an incorrect certificate, or a loop between two redirect rules. First, establish whether the proxy is passing the protocol and whether WordPress is taking it into account.
- The client opens the site over HTTPS.
- The reverse proxy accepts the TLS connection and often forwards the request to the origin over HTTP.
- PHP does not receive or does not account for `X-Forwarded-Proto: https`.
- WordPress sees HTTP and redirects to HTTPS again.
How to Diagnose the Problem Before Making Changes
Check the redirect chain from the command line. The command displays the HTTP headers and the address of each redirect:
curl -I -L --max-redirs 10 https://example.com
What to Look For in the Response
If the same URL appears in several `Location` responses and the number of redirects keeps increasing, a loop is likely. Check whether the scheme changes from `http://` to `https://`, and whether `www`, trailing slashes, or other parts of the address are added or removed.
Temporarily check the protocol values on the server through a protected diagnostic endpoint or WP-CLI. Do not leave such an endpoint publicly accessible: it may expose server variables and configuration details.
- `is_ssl()` should return `true` during a request made through HTTPS.
- `$_SERVER['HTTPS']` should be set or have the value `on`, or an equivalent value for your stack.
- `HTTP_X_FORWARDED_PROTO` should contain `https` if the proxy terminates TLS.
<?php
require_once __DIR__ . '/wp-load.php';
header('Content-Type: text/plain; charset=utf-8');
var_dump(is_ssl());
var_dump($_SERVER['HTTPS'] ?? null);
var_dump($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? null);
Configure the Reverse Proxy to Pass the Protocol
The proxy must pass the protocol used by the client to the backend. The most common option is the `X-Forwarded-Proto: https` header. However, the proxy should overwrite this header itself rather than simply accepting a value supplied by the client.
For Nginx, the values are usually passed in the proxy configuration block. The exact parameters depend on your setup, but the principle looks like this:
- Pass the `Host` header so WordPress generates correct absolute URLs.
- Pass the client’s real IP address in separate headers if this is required for logging or security.
- For HTTPS requests, set `X-Forwarded-Proto` to `https` rather than trusting a value received from the Internet.
- On the origin server, restrict access so that service headers can be received only from a trusted proxy.
location / {
proxy_pass http://wordpress_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Cloudflare and Load Balancers
Cloudflare usually passes scheme information through `X-Forwarded-Proto` or its own service headers. In a load balancer configuration, the header name and the way it is generated may differ. Check the actual HTTP request received by the origin rather than relying only on settings in the control panel.
If multiple proxies sit in front of WordPress, determine which one is the trusted source of protocol information. A chain such as `http, https` may require separate parsing rather than a simple comparison of the entire value with the string `https`.
Tell WordPress That the Request Was HTTPS
If the web server does not set `$_SERVER['HTTPS']` automatically, add a compatible check to `wp-config.php`. Place the code before the line `/* That's all, stop editing! Happy publishing. */` or its equivalent.
if (
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https'
) {
$_SERVER['HTTPS'] = 'on';
}
Use the Header Safely
Do not add this logic without verifying the trusted infrastructure. A client can try to send `X-Forwarded-Proto: https` itself. If the origin server is directly accessible and accepts this header without filtering, an attacker could influence how the scheme is detected.
A more reliable approach is to allow access to the origin only from reverse proxy addresses, overwrite the header at the proxy, and discard the externally supplied value. If the header can contain a list of values in your infrastructure, first confirm the format with the proxy administrator instead of copying this simple check unchanged.
- Do not use several conflicting snippets to determine HTTPS at the same time.
- Do not set `$_SERVER['HTTPS'] = 'on'` for every request if the backend also serves genuine HTTP.
- After editing, check the PHP syntax and restart the required services if your stack requires it.
Check WordPress URLs, Cache, and Cookies
In the database, the `home` and `siteurl` values must use the same canonical scheme and domain that users see, such as `https://example.com`. If one value is still set to `http://`, redirects may continue even after the reverse proxy is fixed.
You can check the addresses with WP-CLI:
wp option get home
wp option get siteurl
What to Clear After the Change
Clear the WordPress page cache, web server cache, CDN cache, and reverse proxy cache if one is used. An old 301 redirect may remain cached in the browser, so test in a private window or with `curl`.
Also check the cookie domain, HTTP authentication rules, and plugins that force HTTPS. Two separate redirect mechanisms—for example, a proxy rule and a WordPress plugin—can create a loop or an unpredictable redirect sequence.
- There should be one canonical site address: HTTPS and the required domain variant.
- Rules for `www` and non-`www` should not redirect to each other.
- Remove any temporary diagnostic code after testing.
Common Mistakes and the Final Check
The problem most often persists because only one layer was changed. For example, a header check was added to `wp-config.php`, but Nginx does not pass that header; or Cloudflare works correctly, but the origin is directly accessible and receives forged service values.
Check the site in several scenarios: the home page, a post page, the admin area, a request without cache, and a request through the CDN. Each scenario should use one HTTPS scheme and have no repeated redirects.
- Review the chain with `curl -I -L` and confirm that it ends with a 200 response rather than a repeating 301 or 302.
- Check `is_ssl()` through the public path used by visitors.
- Confirm that `home` and `siteurl` match the canonical HTTPS address.
- Check for duplicate redirects in Nginx, Cloudflare, the load balancer, WordPress, or a plugin.
- Restrict access to the origin and do not trust `X-Forwarded-Proto` from arbitrary clients.
- After fixing the issue, clear all cache layers and repeat the checks.
Frequently asked questions
Do I always need to add code to wp-config.php?
No. If the web server or reverse proxy correctly sets `$_SERVER['HTTPS']`, no additional code is required. First check `is_ssl()` and the actual request headers.
Can I simply set `$_SERVER['HTTPS'] = 'on'` for every request?
This is unsafe if the origin can serve HTTP or receive incorrect requests. It is better to set the value only after checking a header from a trusted proxy.
Why does the redirect loop remain after changing wp-config.php?
The cause may be a cached 301 in the browser or CDN, incorrect `home` and `siteurl` values, a duplicate redirect rule, an incorrect proxy header, or direct access to the origin that bypasses the required route.
Do I need to change the WordPress address in the database?
Check that `home` and `siteurl` use the correct canonical address with `https://`. Change them carefully and account for serialized data if you perform a bulk replacement in the database.
Conclusion
A redirect loop in WordPress behind a reverse proxy is usually not caused by SSL itself, but by the loss of information about the original protocol between the proxy and PHP. The fix has three parts: the proxy must pass HTTPS correctly, WordPress must safely account for that signal, and the site URLs and cache must match the canonical HTTPS address.
After making changes, check the actual request chain, the value returned by `is_ssl()`, the `home` and `siteurl` settings, and the protection of the origin against forged forwarded headers. This resolves the loop without accidentally disabling HTTPS or creating new security problems.