/ Redirect Checker Fixes / HTTPS Redirects

How to Fix HTTPS Redirects

The classic problem: http://example.comhttps://example.comhttps://www.example.comhttps://www.example.com/page resolves in three hops. Each hop adds 100-500ms latency. Each leaks tiny amounts of link equity. The fix is collapsing to a single hop that goes directly to the final canonical, then enabling HSTS so browsers skip the redirect entirely on repeat visits.

1. Audit the current chain

Step 1
Test the chain length
curl -IL http://example.com 2>&1 | grep -E "^HTTP|^location:|^Location:"
Should show 1-2 status code lines maximum. If 3+, you have a chain to collapse.
Step 2
Identify each hop's source
Run Redirect Checker. Findings list each hop with the source rule (CDN page rule, .htaccess, nginx config, etc).

2. Collapse to single hop at the edge

Best practice: HTTPS upgrade happens at the edge (CDN), once, sending directly to the final canonical URL.

Cloudflare

Rules → Redirect Rules → Create rule

When: HTTP scheme equals "http"
Then: Static redirect
  Target URL: https://www.example.com${http.request.uri}
  Status code: 301
  Preserve query string: enabled

This single rule handles:
  - HTTP → HTTPS upgrade
  - non-www → www canonicalisation  
  - Path preservation
  - Query string preservation

nginx — single-hop HTTPS + canonical host

server {
  listen 80;
  listen [::]:80;
  server_name example.com www.example.com;
  return 301 https://www.example.com$request_uri;
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  server_name example.com;
  # non-canonical https → canonical https
  return 301 https://www.example.com$request_uri;
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;
  server_name www.example.com;
  # actual content serving
  root /var/www/html;
}

Apache .htaccess — single-hop

# Force HTTPS + www in one rule
RewriteEngine On
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

3. Disable conflicting layers

⚠️ After adding the edge-level single-hop rule, disable HTTPS redirects at all other layers. Otherwise you re-introduce chains.
# Check for redundant rules in:
# - .htaccess (if Cloudflare handles HTTPS)
# - WordPress functions.php (force_ssl_admin etc)
# - Application middleware (Express/Django HTTPS forcing)
# - Load balancer health check configs
# - DNS A records pointing to non-CDN IPs

4. Enable HSTS

HSTS tells browsers "use HTTPS for this domain for the next N seconds". After first visit, the browser internally rewrites http:// to https:// — no redirect needed.

nginx

server {
  listen 443 ssl;
  server_name www.example.com;
  
  # Initial conservative HSTS: 1 hour
  add_header Strict-Transport-Security "max-age=3600" always;
  
  # After 1 week of HTTPS stability:
  # add_header Strict-Transport-Security "max-age=31536000" always;  # 1 year
  
  # After 1 month confident:
  # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
  
  # Ready for preload:
  # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

Apache

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

Cloudflare

SSL/TLS → Edge Certificates → HTTP Strict Transport Security (HSTS) → Enable
  Max Age: 12 months
  Include subdomains: enabled (only if ALL subdomains have HTTPS)
  Preload: enabled (only if confident — see warning below)
  No-Sniff: enabled

5. Submit to HSTS preload list

Once HSTS is stable with long max-age and includeSubDomains, submit to hstspreload.org. Major browsers ship the list, so even first-time visitors skip the HTTP attempt entirely.

⚠️ HSTS preload is essentially permanent. Removal takes weeks/months and isn't guaranteed in older browser versions. Only preload after confirming ALL current and future subdomains will support HTTPS forever.

Process:

  1. Run HTTPS with HSTS for 30+ days
  2. Set HSTS header with max-age=31536000, includeSubDomains, preload
  3. Test all subdomains over HTTPS
  4. Visit hstspreload.org, submit domain
  5. Confirmation arrives within days; rollout to browsers over months

6. CDN-specific gotchas

Cloudflare Flexible SSL mode

Cloudflare→Browser is HTTPS, Cloudflare→Origin is HTTP. If your origin redirects HTTP→HTTPS based on local scheme, it sees HTTP from Cloudflare and tries to redirect, causing loop.

# Fix: trust X-Forwarded-Proto header instead of $scheme
if ($http_x_forwarded_proto = "http") {
  return 301 https://$host$request_uri;
}

# Or: use Cloudflare "Full" mode so origin connection is HTTPS

Cloudflare Page Rule vs Always Use HTTPS

If "Always Use HTTPS" is enabled AND a custom page rule does the same thing, you may double-redirect. Use one or the other.

7. Verify single-hop resolution

Step 1
curl trace
curl -IL http://example.com/page 2>&1 | grep -E "^HTTP|^location:" -i

# Expected (single hop):
# HTTP/1.1 301 Moved Permanently
# location: https://www.example.com/page
# HTTP/2 200
Step 2
HSTS header check
curl -I https://www.example.com | grep -i "strict-transport"
# Should show: strict-transport-security: max-age=...
Step 3
SSL Labs scan
SSL Labs server test grades HTTPS configuration. Aim for A or A+. HSTS earns points; preload gets the highest grade.
💡 Order of operations matters: get HTTPS working solidly first, then add HSTS with a short max-age (1 hour), test thoroughly, then ramp max-age to 1 year, then add includeSubDomains, then preload. Don't preload until certain.

↪️ Re-run the Redirect Checker

Verify HTTPS chain is single-hop.

Run Redirect Checker →
Related Guides: Redirect Checker Fixes  ·  Fix www Canonicalisation  ·  Fix Mixed Content  ·  Redirect Checker Guide
💬 Got a problem?