The correct, fast way to force HTTPS is a tiny dedicated server block on port 80 that does nothing but return 301. Don’t use rewrite or if for this — return is unambiguous and cheaper.
# Plain HTTP: redirect everything to HTTPS.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
# Your real site.
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
# ssl_certificate ...; (see the TLS recipe)
location / {
# ...
}
}
Why this shape:
$hostpreserves whichever hostname was requested;$request_uripreserves the full path and query string, sohttp://example.com/a?b=1→https://example.com/a?b=1.- 301 is a permanent redirect (browsers and search engines cache it). Use 302 only if the switch is temporary.
- A separate server block is faster and clearer than
if ($scheme = http)inside one block — and avoids the well-known “if is evil” pitfalls. - Want HSTS so browsers skip the HTTP hop next time? Add
add_header Strict-Transport-Security "max-age=63072000" always;to the HTTPS block (see headerforge for the full header set).
sudo nginx -t && sudo nginx -s reload