Skip to main content
All fixes

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 Audit

How to fix it

  1. 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. 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. 3

    Add the redirect in Apache

    In a port-80 VirtualHost: RewriteEngine On and RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]. Or use mod_alias: Redirect permanent / https://example.com/. Reload Apache.

  4. 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. 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. 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