You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nginx中HTTP与非WWW网址重定向的最佳实践咨询

Nginx Redirect Best Practices: All Variants to 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

  1. 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.
  2. 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.
  3. HSTS Hardening

    • Added the always parameter to ensure HSTS headers are sent even for error responses (browsers might ignore them otherwise).
    • includeSubdomains enforces HTTPS for all subdomains (only use this if all your subdomains use HTTPS!).
    • preload prepares your domain for inclusion in browser HSTS preload lists (submit to the preload list only if you’re fully committed to HTTPS everywhere).
  4. Avoided Dangerous if Statements

    • Replaced your if (!-f $request_filename) block with try_files and a named location. Nginx’s if directive can have unexpected behavior in location blocks, so try_files is the safer, recommended approach.
  5. 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 with include /etc/nginx/snippets/ssl-params.conf;.

Critical Checks Before Deploying

  • Use a SAN Certificate: Ensure your SSL certificate includes both example.com and www.example.com (otherwise, visitors to https://example.com will get SSL errors).
  • Test All Redirects: Use curl -I to verify every variant redirects correctly:
    curl -I http://example.com
    curl -I https://example.com
    curl -I http://www.example.com
    
    All should return a 301 to https://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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:02:41