SSL, TLS, security headers
Force HTTP-to-HTTPS redirects on every hostname you serve
Plain HTTP requests must 301 to HTTPS, including apex, www, and every subdomain. Pair with HSTS so browsers cache the upgrade.
What's happening
An HTTP-to-HTTPS redirect tells clients that arrive on plain HTTP to retry over HTTPS. Without it, requests to http://example.com return either a plain HTTP page (insecure), a connection refused (broken), or a default server page (embarrassing). The first request a browser makes to a hostname is often plain HTTP — a typed URL, a copy-pasted link without scheme, an HTTP-only legacy bookmark — so this redirect is the gatekeeper for HTTPS adoption.
The redirect must be a 301 (permanent) rather than 302 (temporary), because intermediaries cache 301s and serve them faster on subsequent visits. Pair the redirect with HSTS so the browser remembers the upgrade and skips the HTTP request entirely on future visits.
The fix is a one-line redirect in nginx or Apache, plus making sure every hostname (apex, www, subdomains) and every port (80) is covered. Forgetting one variant leaves a window for SSL-stripping attacks.
Why it matters
First-time visitors who arrive via plain HTTP see whatever your port-80 listener decides to serve — typically a default page or a connection refused. Bounce rate spikes, and the user may not bother retrying with https://.
Without the redirect, mixed-protocol marketing links (http vs https) split SEO authority across two URL variants. Search engines treat http://example.com and https://example.com as different URLs unless redirected.
PCI-DSS, OWASP, and every modern compliance framework require HTTPS-only on any page handling credentials or payment. Missing the redirect is a clear control failure.
Common causes
- Web server has a port-80 listener with no redirect rule.
- Redirect exists for the apex but not www, or the other way around.
- Subdomains are served from a separate listener that was forgotten in the migration.
- A 302 was used instead of a 301, causing repeated round-trips on every visit.
- Redirect drops query strings or path components, breaking deep links.
Detect this on your site
Run a quick scan with the Full Site Audit. The tool surfaces this exact issue with the records and context needed to apply the fix below.
Open Full Site AuditHow to fix it
- 1
Test every hostname and path
Run
curl -sI http://example.com/,curl -sI http://www.example.com/, and a sample subdomain. Each should return 301 with a Location header pointing to the corresponding HTTPS URL preserving the path. If any returns 200 or 404, the redirect is missing on that listener. - 2
Add the redirect block in nginx
Add a dedicated server block listening on port 80 that returns a 301 to the HTTPS equivalent:
server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }. Reload nginx. - 3
Add the redirect in Apache
In a port-80 VirtualHost:
RewriteEngine OnandRewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]. Or use mod_alias:Redirect permanent / https://example.com/. Reload Apache. - 4
Cover subdomains and edge cases
Make sure every hostname listed in DNS — apex, www, mail, api, app, internal — has an HTTP-to-HTTPS redirect. Use a wildcard server block (
server_name _;as a catch-all that issues the redirect for any unrecognized HTTP request. - 5
Verify the redirect preserves the path
Test
curl -sI http://example.com/some/deep/path?query=1. The Location header must include/some/deep/path?query=1. A redirect that drops the path silently breaks every link in inbound emails and search results. - 6
Add HSTS so future requests skip HTTP entirely
Once redirects are in place, set HSTS on HTTPS responses with
max-age=63072000; includeSubDomains; preload. After the first HTTPS visit, the browser remembers and never makes the HTTP request again. Submit to hstspreload.org for extra protection.
Example
# Catch-all HTTP listener that 301s to HTTPS
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# ...rest of HTTPS config...
}Nginx HTTP-to-HTTPS redirect with paired HSTS
Frequently asked
301 (Moved Permanently). Browsers and search engines cache the redirect which is what you want. 302 forces a fresh round-trip every visit and dilutes SEO ranking signals.
Only if the caller does not follow redirects. Most HTTP clients (curl with -L axios Go's net/http) follow 301s by default. If a webhook sender does not update your registration to use the https:// URL directly so no redirect is needed in the hot path.
Related fixes
SSL, TLS, security headers
Add HSTS header to enforce HTTPS and prevent SSL stripping
SSL, TLS, security headers
Increase HSTS max-age to meet preload list requirements
SSL, TLS, security headers
Fix mixed content warnings on HTTPS pages
SSL, TLS, security headers
Fix SSL certificate hostname mismatch errors in browsers