2026年11月12日
2026年11月12日
WordPress HTTP to HTTPS 301 Redirect: .htaccess, Nginx and Cloudflare
This article is a localized edition of the Japanese original. Read the Japanese original
Once an SSL certificate is installed, 301-redirect requests for http:// to https:// so the site has a single URL. A wrong setup leads to "too many redirects" (ERR_TOO_MANY_REDIRECTS) or a mix of www and non-www URLs. This article covers the order of work, Apache (.htaccess) and Nginx settings, how to avoid loops behind load balancers and Cloudflare, and how to verify the result.
In the examples, the canonical URL is https://example.com (without www). The rules for standardizing on www are shown as well.
Order of work
- Confirm HTTPS works first: make sure
https://example.com/opens in a browser without a certificate warning. If you also redirect the www URL, the certificate must includewww.example.comtoo. - Switch the WordPress URLs to https: in the admin, go to Settings → General and change "WordPress Address (URL)" and "Site Address (URL)" to
https://example.com. - Set up the redirect on the server: add the .htaccess or Nginx configuration shown below.
- Verify: use curl to confirm every entry point reaches the canonical URL with a single 301.
Back up your files and database before you start.
Check the current behavior
First, see how the site responds now. -I tells curl to show only the headers.
# Show only the status code and redirect target
curl -sI http://example.com/ | grep -iE '^(HTTP|location)'
# Follow redirects and show the final URL and redirect count (a loop stops at the limit)
curl -sIL --max-redirs 10 -o /dev/null -w '%{http_code} %{num_redirects} %{url_effective}\n' http://www.example.com/
WordPress settings
Change the URLs in Settings → General
Set "WordPress Address (URL)" and "Site Address (URL)" to URLs that start with https://. If the login screen appears after saving, log in again at the https URL. If WP-CLI is available, you can check and change them with these commands.
wp option get home
wp option get siteurl
wp option update home 'https://example.com'
wp option update siteurl 'https://example.com'
If wp-config.php defines WP_HOME or WP_SITEURL, those values take precedence and the admin fields cannot be edited. In that case, change the values in wp-config.php to https.
Once the site can be served over HTTPS, Tools → Site Health may show a button for switching the site to HTTPS.
Replace http URLs in your content
If older posts still contain image URLs like http://example.com/wp-content/uploads/..., browsers show mixed content warnings. When replacing them with WP-CLI, check the number of matches with --dry-run first.
wp search-replace 'http://example.com' 'https://example.com' --all-tables --dry-run
# Run it after checking the counts
wp search-replace 'http://example.com' 'https://example.com' --all-tables
Redirect with Apache (.htaccess)
Add the rules to the root .htaccess before the # BEGIN WordPress line. Anything inside the WordPress block is overwritten when, for example, permalink settings are saved.
Standardize on non-www
Both http requests and www requests go to https://example.com with a single 301.
# Redirect HTTP or www requests to https://example.com with a single 301
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]
</IfModule>
%{REQUEST_URI} holds the path, and mod_rewrite carries over the query string (such as ?x=1) automatically.
Standardize on www
# Redirect HTTP or non-www requests to https://www.example.com with a single 301
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,L]
</IfModule>
Behind a load balancer or proxy
When a load balancer or CDN terminates HTTPS and forwards requests to the server over HTTP, every request looks like HTTP to the server, so %{HTTPS} off always matches and the redirect loops. In that setup, decide based on the X-Forwarded-Proto header added by the proxy.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
Requests without the header (such as load balancer health checks) are redirected too, so if a health check fails because of the 301, add a condition that excludes its path.
Redirect with Nginx
A server block for HTTP sends requests to HTTPS, and another block sends HTTPS requests for www to the canonical host.
# HTTP goes to https://example.com with or without www
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
# https://www.example.com also goes to https://example.com
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 301 https://example.com$request_uri;
}
# Main site
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Your usual settings: root, location, PHP-FPM and so on
}
http2 on; is the syntax for Nginx 1.25.1 and later. On older Nginx, write listen 443 ssl http2; instead (newer versions print a deprecation warning for listen ... http2). To standardize on www, swap the server_name values of the second and third blocks and point the return targets to https://www.example.com.
After changing the configuration, test the syntax and then reload.
sudo nginx -t && sudo systemctl reload nginx
Prevent redirect loops
Tell WordPress about HTTPS behind a proxy
Behind a load balancer, Cloudflare and similar services, PHP sees an HTTP connection, so WordPress concludes it is being served over HTTP and may keep redirecting to https, for example in the admin. If the proxy sets X-Forwarded-Proto, add the following to wp-config.php above /* That's all, stop editing! */.
<?php
// Make WordPress treat requests the proxy received over HTTPS as HTTPS
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
// Restrict the admin and login to HTTPS
define( 'FORCE_SSL_ADMIN', true );
Anyone can send this header, so use this only when the server cannot be reached directly (requests arrive only through the proxy).
Cloudflare SSL/TLS setting
When Cloudflare's SSL/TLS encryption mode is "Flexible", Cloudflare connects to your server over HTTP. If the server also redirects to HTTPS, every HTTP request from Cloudflare is sent back to HTTPS and the redirect loops. Install a certificate on the server and set the mode to "Full (strict)" (Cloudflare's label as of October 2026).
If it still loops
Narrow it down in this order.
# 1. Are the WordPress URLs set to https?
wp option get home
wp option get siteurl
# 2. Does the loop stop with .htaccess temporarily moved aside?
mv .htaccess .htaccess.bak
# 3. Does the loop stop with all plugins deactivated? (if so, reactivate them one by one to find the cause)
wp plugin list --status=active --field=name > active-plugins.txt # note the active ones first
wp plugin deactivate --all
wp plugin activate plugin-slug # replace plugin-slug with a name saved in active-plugins.txt
When you're done, rename .htaccess.bak back and reactivate the plugins you turned off. If you use an SSL or redirect plugin, it can double up with the server-side redirect and cause a loop, so use only one of the two.
Verify the setup
With https://example.com as the canonical URL, the correct state is that all three of the following entry points reach the canonical URL with a single 301, and the canonical URL returns 200.
for u in http://example.com/ http://www.example.com/ https://www.example.com/; do
curl -sI "$u" | grep -iE '^(HTTP|location)'
done
curl -sI https://example.com/ | grep -iE '^HTTP'
Expected results:
| Requested URL | Response | Redirects to |
|---|---|---|
| http://example.com/ | 301 | https://example.com/ |
| http://www.example.com/ | 301 | https://example.com/ |
| https://www.example.com/ | 301 | https://example.com/ |
| https://example.com/ | 200 | None |
Browsers cache 301s, so while you experiment you can test with R=302 or return 302 and switch to 301 once everything works. When checking in a browser, a private window is less affected by the cache.
Summary
- Confirm HTTPS works, switch both URLs in Settings → General to https, then add the server-side redirect
- On Apache, add rules before
# BEGIN WordPressthat send http and www requests to the canonical URL with a single 301 - On Nginx, use
return 301in a server block for HTTP and one for www - Behind a proxy or Cloudflare, decide by
X-Forwarded-Proto, and set Cloudflare to "Full (strict)" - Use curl to confirm every entry point reaches the canonical URL with a single 301
Related articles
- How to fix the "too many redirects" error
- How to fix mixed content in WordPress
- How to unify www and non-www URLs
- How to set up Cloudflare for WordPress
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)