Nginx中HTTP与非WWW网址重定向的最佳实践咨询
https://www.example.com Great question! Your current configuration works, but we can optimize it for clarity, efficiency, and security while keeping the core goal of redirecting all domain variants (http/https, with/without www) to https://www.example.com. Let’s walk through the ideal setup and why each part matters.
First: Why Your Current Setup Works (and Where It Can Improve)
Your existing 3-server-block setup gets the job done, but it has some redundancy:
- You’re repeating SSL configurations and HSTS headers across multiple blocks.
- Combining
example.com’s HTTP and HTTPS listeners in one block works, but splitting them makes the config easier to read and maintain.
The multi-server_name version you mentioned is more concise, but it has a catch: if you put both example.com and www.example.com in the same HTTPS server block, visitors to https://example.com won’t get redirected—they’ll land directly on your app, which defeats the goal of standardizing on the www subdomain.
The Ideal, Best-Practice Configuration
Here’s a streamlined, secure setup that covers all edge cases:
# Global error log (better to keep this outside server blocks for consistency) error_log /dev/stdout info; # Upstream Django server (keep this as-is) upstream django_server { server app:8001 fail_timeout=0; } # 1. Catch ALL HTTP requests (any domain) and redirect to HTTPS www server { listen 80 default_server; listen [::]:80 default_server; # IPv6 support server_name _; # Matches ANY domain (restrict to example.com www.example.com if needed) # Send HSTS header even for HTTP redirects to enforce HTTPS long-term add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always; return 301 https://www.example.com$request_uri; } # 2. Handle HTTPS requests to non-www example.com, redirect to www server { listen 443 ssl; listen [::]:443 ssl; server_name example.com; # SSL config (use a SAN certificate covering both example.com and www.example.com) ssl_certificate /etc/nginx/ssl/certificate.chained.crt; ssl_certificate_key /etc/nginx/ssl/example.key; # Modern SSL security (disable old, insecure protocols) ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # OCSP stapling for faster SSL handshakes and better security ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always; return 301 https://www.example.com$request_uri; } # 3. Main HTTPS server for www.example.com (handles actual app traffic) server { listen 443 ssl default_server; listen [::]:443 ssl default_server; server_name www.example.com; client_max_body_size 4G; charset utf-8; keepalive_timeout 70; # Reuse SSL config (use an include file here to avoid duplication!) ssl_certificate /etc/nginx/ssl/certificate.chained.crt; ssl_certificate_key /etc/nginx/ssl/example.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; resolver_timeout 5s; add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always; # Gzip config (your existing setup is solid—keep it!) gzip on; gzip_http_version 1.1; gzip_disable "MSIE [1-6]\."; gzip_min_length 256; gzip_vary on; gzip_proxied expired no-cache no-store private auth; gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss; gzip_comp_level 9; # Static files handling location /static/ { root /usr/share/nginx/sasite/; expires 30d; autoindex off; location /static/download/ { location ~* \.(pdf|docx|zip|rar)$ { add_header Content-Disposition 'attachment; filename="$request_filename"' always; } } } # App proxy logic (replaced your if block with try_files + named location—Nginx-recommended!) location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_redirect off; # Try static files first; if not found, proxy to Django try_files $uri $uri/ @django_proxy; } location @django_proxy { proxy_pass http://django_server; } }
Key Optimizations & Why They Matter
Clear, Single-Responsibility Server Blocks
- Each block does one job: handle HTTP redirects, handle non-www HTTPS redirects, or serve the app. No mixing responsibilities, making the config easier to debug and update.
Modern SSL Security
- Disabled outdated TLSv1/TLSv1.1 (they’re no longer secure) and used industry-standard cipher suites.
- Enabled OCSP stapling to reduce SSL handshake time and improve privacy.
HSTS Hardening
- Added the
alwaysparameter to ensure HSTS headers are sent even for error responses (browsers might ignore them otherwise). includeSubdomainsenforces HTTPS for all subdomains (only use this if all your subdomains use HTTPS!).preloadprepares your domain for inclusion in browser HSTS preload lists (submit to the preload list only if you’re fully committed to HTTPS everywhere).
- Added the
Avoided Dangerous
ifStatements- Replaced your
if (!-f $request_filename)block withtry_filesand a named location. Nginx’sifdirective can have unexpected behavior in location blocks, sotry_filesis the safer, recommended approach.
- Replaced your
Reduced Redundancy
- If you want to eliminate duplicate SSL config, create a snippet file (e.g.,
/etc/nginx/snippets/ssl-params.conf) with all your SSL settings, then include it in both HTTPS server blocks withinclude /etc/nginx/snippets/ssl-params.conf;.
- If you want to eliminate duplicate SSL config, create a snippet file (e.g.,
Critical Checks Before Deploying
- Use a SAN Certificate: Ensure your SSL certificate includes both
example.comandwww.example.com(otherwise, visitors tohttps://example.comwill get SSL errors). - Test All Redirects: Use
curl -Ito verify every variant redirects correctly:
All should return a 301 tocurl -I http://example.com curl -I https://example.com curl -I http://www.example.comhttps://www.example.com. - HSTS Preload Caution: Once you submit your domain to the HSTS preload list, it’s very hard to remove. Make sure all your assets (including subdomains) are fully HTTPS-compliant first.
内容的提问来源于stack exchange,提问作者ss7

