⏱️ Reading time: ~11 minutes | 📅 Updated: December 30, 2025
Browsers mark HTTP sites as "Not Secure". Google ranks them lower in search results. Users see warnings and close the tab. And if your site has a form — password, email, credit card number — all that data flies across the network in plain text.
SSL/TLS certificates are free today (thanks to Let's Encrypt). There's no reason to stay on HTTP. But installing a certificate is only half the job. The other half is properly configuring the redirect so all traffic automatically goes through HTTPS.
Seems simple, right? Add one line to the config and you're done. But in practice, this is where problems arise: endless redirect loops, mixed content, loss of search rankings due to improper canonicalization, CDN issues.
This article covers how to do everything right the first time. Specific configs for Apache, Nginx, Cloudflare. What can go wrong and how to fix it.
An HTTP redirect is an instruction to the browser: "The page isn't here, go to this address". Code 301 means "Moved Permanently".
301 (Moved Permanently) — permanent redirect. Search engines pass link weight to the new address. Browsers cache the redirect, so repeat requests go directly to HTTPS.
302 (Found / Temporary Redirect) — temporary. Search engines don't pass weight because they consider it temporary. Wrong choice for HTTP→HTTPS.
307 (Temporary Redirect) — also temporary, but preserves the request method (POST stays POST). Not suitable for regular HTTPS redirect.
308 (Permanent Redirect) — like 301, but preserves method. Can be used, but 301 is more compatible with older browsers.
Common mistake: http://example.com → http://www.example.com → https://www.example.com → https://example.com. Four hops instead of one.
Each redirect adds 50-200ms delay. For mobile users on slow connections, this is critical. Google counts this as part of load time.
Correct approach: one redirect straight to the final address. Decide which version is canonical (with www or without), and redirect everything there.
For Apache, the recommended method is through VirtualHost configuration, not .htaccess. VirtualHost is faster because .htaccess is re-read on every request.
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/ssl/cert.pem
SSLCertificateKeyFile /etc/ssl/key.pem
DocumentRoot /var/www/html
</VirtualHost>
If you're upgrading from Apache 2.2 to 2.4, the access control configuration has changed. This is a common source of "403 Forbidden" errors after upgrade.
| Feature | Apache 2.2 | Apache 2.4 |
|---|---|---|
| Access control | Order allow,deny |
Require all granted |
| Authorization module | mod_authz_host.so |
mod_authz_core.so |
| AllowOverride | AllowOverride All |
AllowOverride None |
| Logging | LogLevel warn |
LogLevel warn rewrite:trace3 |
If Apache sits behind a load balancer or proxy, the client connects to the proxy via HTTPS, and the proxy forwards the request to Apache via HTTP. A regular redirect will create a loop.
First, enable the required modules:
a2enmod proxy proxy_http headers rewrite ssl
systemctl reload apache2
Configuration for reverse proxy:
<VirtualHost *:80>
ServerName example.com
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
RequestHeader set X-Forwarded-Proto "http"
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/ssl/cert.pem
SSLCertificateKeyFile /etc/ssl/key.pem
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
RequestHeader set X-Forwarded-Proto "https"
</VirtualHost>
The backend application checks the X-Forwarded-Proto header and redirects only if it's not "https":
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Nginx is the most popular web server for high-load projects. Redirect setup here is simpler than in Apache.
Use return 301 — this is the recommended option. It's simpler, processes faster (no regex), immediately forms a response and doesn't go through other processing phases.
rewrite is only needed if you require complex logic with regular expressions.
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
HTTP/2 is stable and recommended whenever HTTPS is enabled. Provides noticeable loading speed improvement through request multiplexing.
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off; # for TLS 1.3
keepalive_timeout 65;
keepalive_requests 1000;
gzip on;
gzip_types text/plain text/css application/json application/javascript;
root /var/www/html;
}
HTTP/3 in Nginx is still experimental. Use only if you're ready for testing and potential issues.
server {
listen 443 ssl http2;
listen 443 quic reuseport;
ssl_protocols TLSv1.3;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
add_header Alt-Svc 'h3=":443"; ma=86400';
}
The Alt-Svc header tells the browser that HTTP/3 is available. The browser will try the next connection via QUIC.
If Nginx works as a load balancer in front of multiple backend servers:
upstream backend {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
Note the X-Forwarded-Proto — the backend receives information about the original protocol and can properly form links.
If you use Cloudflare or another CDN, you can redirect at the CDN level — before the request even reaches your server.
Client
↓
Cloudflare (redirects, edge logic)
↓
Nginx (origin, app logic)
↓
Upstream / Backend
Benefits of CDN-level redirect: faster (response from nearest edge server), less load on origin.
Most common problem: CDN redirects to HTTPS, and the server also redirects because it sees an HTTP connection from the CDN. Result — endless loop.
Solution: set SSL mode to "Full (strict)" in Cloudflare. This means:
"Flexible" mode (Cloudflare→server via HTTP) is a compromise for servers without a certificate. If you have a certificate — use Full (strict).
In Cloudflare, you can set up Page Rules for automatic redirect:
| URL Pattern | Action |
|---|---|
http://example.com/* |
Always Use HTTPS |
http://www.example.com/* |
Always Use HTTPS |
https://www.example.com/* |
301 Redirect → https://example.com/$1 |
The first two rules convert HTTP to HTTPS. The third canonicalizes www to the non-www version (or vice versa, depending on your choice).
HSTS (HTTP Strict Transport Security) is a header that tells the browser: "Never connect to this domain via HTTP. Even if the user types http:// — use https://".
Strict-Transport-Security: max-age=31536000; includeSubDomains
max-age=31536000 — the browser will remember this policy for one year (in seconds).
includeSubDomains — the policy applies to all subdomains.
For Nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
HSTS Preload means adding your domain to a list built into browsers. The browser will use HTTPS even on the first visit, without needing to receive the header first.
⚠️ In most cases, we DO NOT recommend HSTS Preload.
Recommended only for rock-solid domains where you're 100% certain HTTP will never be needed.
Start with a short max-age (86400 — one day). Make sure everything works. Gradually increase to one year.
After configuring redirects, be sure to verify everything works correctly. Here are the tools we recommend.
| Tool | What it checks |
|---|---|
| SSL Labs (Qualys) | TLS, certificate chain, protocols, ciphers — deep audit |
| Hardenize | TLS, HSTS, DNSSEC, cookies, PKI — comprehensive security assessment |
| Observatory (Mozilla) | Headers, CORS, TLS — quick security health-check |
| SecurityHeaders.com | HSTS, CSP, X-Frame-Options — HTTP security headers |
The simplest way — curl from terminal:
curl -IL https://example.com | grep -Ei 'HTTP/|Location|Server|cf-'
The -I flag shows only headers, -L follows redirects. You'll see the entire chain: HTTP/1.1 301, Location, then HTTP/2 200 on the final page.
| Tool | What it checks |
|---|---|
| WebPageTest | RUM/LAB metrics, filmstrip, waterfall — detailed breakdown |
| Lighthouse (Chrome DevTools) | SEO, performance, accessibility — all in one |
| GTmetrix | PageSpeed + Waterfall — practical optimization plan |
Browser shows error "This page isn't working" with code ERR_TOO_MANY_REDIRECTS.
Causes:
Diagnosis: curl -IL http://example.com — see how many redirects and where.
Solution: redirect should be in one place only. If CDN — only on CDN. If server — only on server. When using Cloudflare — SSL mode Full (strict).
Page loads via HTTPS, but some resources (images, scripts, styles) load via HTTP. Browser blocks or shows warning.
Diagnosis:
Solution:
Content-Security-Policy: upgrade-insecure-requests — browser will automatically replace http with httpsCertificate issued for example.com, but not for www.example.com. Visiting www shows browser warning.
Solution: get a certificate for both variants. Let's Encrypt allows including multiple domains: certbot -d example.com -d www.example.com
http://example.com/about → https://example.com/ (without /about)
User loses context, search engines see 404 for old URLs.
Solution: make sure the redirect preserves URI:
# Nginx — correct
return 301 https://example.com$request_uri;
# Nginx — incorrect (loses path)
return 301 https://example.com;
Let's Encrypt — free certificates with automatic renewal. This is the standard for most websites.
Certificates are issued for 90 days, so automatic renewal is critically important. Certbot usually sets up a cron job automatically:
# Check if auto-renewal is configured
systemctl list-timers | grep certbot
# Or check cron
cat /etc/cron.d/certbot
Test renewal run (without actual renewal):
certbot renew --dry-run
A wildcard certificate (*.example.com) covers all single-level subdomains: blog.example.com, shop.example.com, api.example.com.
When we recommend:
Important: wildcard covers only one level. *.example.com covers blog.example.com, but does NOT cover dev.blog.example.com.
Let's Encrypt issues wildcard certificates via DNS challenge:
certbot certonly --manual --preferred-challenges dns \
-d example.com -d *.example.com
For automating DNS challenge, you need a plugin for your DNS provider (Cloudflare, Route53, DigitalOcean, etc.).
Before launching, go through this list:
We'll configure redirects, SSL and HSTS on our servers. Or help you figure out your configuration.
💬 Not sure which option you need?
💬 Contact us and we'll help with everything!
A proper 301 redirect passes ~90-99% of link weight. Google officially confirms that HTTPS is a ranking factor. Short-term ranking drops are possible (while Google re-indexes), but in the long term HTTPS is better for SEO.
For most sites — no. Let's Encrypt provides the same level of encryption. Paid certificates (OV, EV) provide extended company validation, but this only matters for banks, financial institutions, and large e-commerce sites.
301 redirect automatically forwards old HTTP links. But it's better to update: internal links on the site, links in social media and profiles, Google Search Console and Analytics. Sitemap should contain only https:// URLs.
Open DevTools → Network → select any request → Response Headers. Look for Strict-Transport-Security. Or check on securityheaders.com — it will show the score and all headers.
Possible causes: long redirect chain (check curl -IL), slow TLS handshake (outdated settings), server far from users (need CDN). Also check OCSP stapling — without it, the browser makes an extra request to verify the certificate.
Yes, but slowly. Set max-age=0 and wait for the old term to expire in browser caches. If you used HSTS Preload — the process will take months. That's why you should start with a short max-age.
Still have questions? Chat with us — we'll help with configuration.