Many developers encounter this common scenario: HTTP non-WWW traffic redirects properly to HTTPS WWW, but HTTPS non-WWW fails to redirect. This happens because of subtle configuration nuances in Nginx's SSL server blocks.
The original configuration has two key files:
# default.conf (handling HTTP)
server {
listen 80;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
The SSL configuration has a critical flaw - the redirect server block isn't properly listening on port 443:
# Problematic default-ssl.conf
server {
server_name example.com;
return 301 https://www.example.com$request_uri;
# Missing 'listen 443 ssl' directive!
}
Here's the corrected version that handles all cases:
# HTTP to HTTPS WWW redirect
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
# HTTPS non-WWW to WWW redirect
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
return 301 https://www.example.com$request_uri;
}
# Main HTTPS WWW server
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Rest of your configuration...
}
The key improvements are:
- Explicit
listen 443 ssl
directive for the redirect server - Proper SSL certificate declarations in the redirect block
- Complete separation of redirect and main content server blocks
After making changes, always:
sudo nginx -t
sudo systemctl restart nginx
Then verify with curl:
curl -I http://example.com
curl -I https://example.com
Ensure your SSL certificate covers both domains:
- example.com
- www.example.com
Either use a wildcard certificate (*.example.com) or specify both as Subject Alternative Names (SANs).
Many developers encounter a peculiar situation where HTTP non-www URLs redirect properly to HTTPS www versions, but HTTPS non-www URLs stubbornly refuse to redirect. Here's why this happens and how to fix it permanently.
The key issue lies in how Nginx processes SSL certificates and server blocks. Your current setup has two critical gaps:
# Missing server_name for SSL version
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /path/to/cert.crt;
ssl_certificate_key /path/to/key.key;
return 301 https://www.example.com$request_uri;
}
Here's the corrected version that handles all combinations:
# HTTP non-www → HTTPS www
server {
listen 80;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
# HTTPS non-www → HTTPS www (critical missing piece)
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /srv/www/example.com/keys/ssl.crt;
ssl_certificate_key /srv/www/example.com/keys/www.example.com.key;
return 301 https://www.example.com$request_uri;
}
# Primary HTTPS www configuration
server {
listen 443 ssl;
server_name www.example.com;
# ... rest of your existing SSL config
}
For this to work seamlessly, ensure your SSL certificate covers both:
- example.com (root domain)
- www.example.com (subdomain)
Verify all scenarios work using curl:
curl -I http://example.com # Should 301 to https://www
curl -I https://example.com # Should 301 to https://www
curl -I https://www.example.com # Should return 200
For production environments, consider adding these optimizations to your SSL config:
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
Watch out for these frequent mistakes:
- Certificate not covering both domain variations
- Missing $request_uri in redirects
- Not specifying listen 443 ssl in redirect server block
- Incorrect server_name ordering in Nginx config