Server config

.htaccess and Nginx Redirect Rules for Any Site

If your platform doesn't have a built-in HTTPS toggle, the redirect belongs at the web server level, applied to every request before it reaches the application.

Apache (.htaccess)

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Nginx

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}

Use a 301 (permanent), not a 302 — this tells search engines the move is final and passes ranking signals to the new URL. Then double-check every internal link, stylesheet, and script reference is also HTTPS, or the padlock will show as "partially secure" due to mixed content.

Why this needs to happen before the request reaches your application

Handling the redirect in your application code (a framework middleware, a CMS setting) means the full request — headers, cookies, sometimes a request body — has already been received in plain text before your code ever runs. Terminating the redirect at the web server layer instead means the server can respond with the 301 immediately, without ever handing the request further down the stack. For most sites the practical security difference is small, but it's also simply faster: the server doesn't spin up a PHP process, load a framework, or query a database just to say "go to https://" — it's a couple of lines evaluated before any of that machinery starts.

Detecting HTTPS behind a load balancer or CDN

If your server sits behind a load balancer, reverse proxy, or CDN that terminates TLS itself, the %{HTTPS} variable Apache checks (and the equivalent in Nginx) may never actually be set to "on" — because as far as your origin server can tell, every request arrives as plain HTTP, even the ones the visitor made over HTTPS. In that case, check the X-Forwarded-Proto header instead:

# Apache
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# Nginx
if ($http_x_forwarded_proto = "http") {
    return 301 https://$host$request_uri;
}

Using the wrong check here is the single most common cause of a redirect loop on this exact setup: the origin keeps seeing "HTTP" on every request (since the load balancer already stripped the encryption before forwarding), keeps issuing the same redirect, and the load balancer keeps handing it right back.

A note on rule order in Apache

If your .htaccess already has other rewrite rules — a CMS's own routing rules, for instance — the HTTPS redirect generally needs to come before them, not after. Apache processes rules top to bottom, and a routing rule that matches first can prevent the HTTPS check further down from ever being evaluated for that request.

Comments

Loading comments…