When configuring Nginx for production, one common requirement is enforcing consistent domain access - either always with WWW or always without. Here's a complete solution that handles both HTTP to HTTPS and non-WWW to WWW redirects.
# HTTP - non-WWW to HTTPS WWW
server {
listen 80;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
# HTTP - WWW to HTTPS WWW
server {
listen 80;
server_name www.example.com;
return 301 https://www.example.com$request_uri;
}
# HTTPS - non-WWW to WWW
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 block
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
root /var/www/html;
index index.php index.html;
# Your regular configuration here
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP handling if needed
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}
}
The configuration handles all possible cases:
- http://example.com → https://www.example.com
- http://www.example.com → https://www.example.com
- https://example.com → https://www.example.com
1. Missing return 301: Using rewrite instead of return for redirects is less efficient
2. SSL certificate mismatch: Ensure your certificate covers both www and non-www versions
3. Infinite redirect loops: Test all possible URL combinations
Before applying changes, always test with:
sudo nginx -t
Then verify all redirect scenarios using curl:
curl -I http://example.com
curl -I http://www.example.com
curl -I https://example.com
Using separate server blocks for redirects is more efficient than conditional rewrites within a single block. The 301 redirects are cached by browsers, reducing subsequent redirect overhead.
For high-traffic sites, consider adding these headers to your main server block:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
When implementing redirects in Nginx, we often encounter edge cases where different URL patterns don't consistently redirect to the desired www-prefixed HTTPS version. The core issue manifests when:
- Bare domain (site.com) redirects correctly to https://www.site.com
- Subpages (site.com/index.php) redirect only to http://www.site.com (missing HTTPS)
Here's the proper way to handle both www and HTTPS enforcement:
# HTTP to HTTPS + WWW redirect for bare domain
server {
listen 80;
server_name site.com;
return 301 https://www.site.com$request_uri;
}
# HTTP to HTTPS + WWW redirect for www domain
server {
listen 80;
server_name www.site.com;
return 301 https://www.site.com$request_uri;
}
# HTTPS configuration
server {
listen 443 ssl;
server_name www.site.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
root /home/site/public_html;
index index.php;
# Additional SSL config...
}
# Catch-all for HTTPS requests to bare domain
server {
listen 443 ssl;
server_name site.com;
return 301 https://www.site.com$request_uri;
}
1. The $request_uri Variable
Using $request_uri
instead of $1
is crucial because it:
- Preserves the original request path
- Includes query strings if present
- Is more reliable than regex captures
2. Separate Server Blocks
Having distinct server blocks for each scenario ensures:
1. HTTP → HTTPS
2. non-WWW → WWW
3. All combinations handled explicitly
After implementing, verify with:
curl -I http://site.com
curl -I http://www.site.com
curl -I https://site.com
curl -I https://www.site.com/test?param=value
All should return 301 redirects to https://www.site.com
with the path and query string preserved.
- Mixing rewrite and return directives (stick with return 301)
- Forgetting the HTTPS server block for the bare domain
- Not testing with various URL patterns