首页 文章

Nginx重定向(非www到www)不与Certbot一起使用

提问于
浏览
0

我有一个运行Python / Django / uWSGI / Nginx设置的网站 . 我还使用Certbot在我的网站上启用https . 我从非www重定向到www(例如"example.com"到"www.example.com")导致"Bad Request (400)"消息,即使我无法发现与Nginx / Certbot文档的任何偏差 . 这是我的 sites-available Nginx代码的相关部分:

server {
    listen 80;
    server_name example.com www.example.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/myname/example;
    }

    location / {
        include        uwsgi_params;
        uwsgi_pass     unix:/run/uwsgi/activities.sock;
    }

    listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; #managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; #managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    } # managed by Certbot

}

我找到了类似的StackOverflow答案(Nginx: redirect non-www to www on https),但没有一个解决方案适合我 . 我有example.com和www.example.com的SSL证书 . 我还尝试根据该答案中的注释为example.com创建一个单独的443 ssl服务器块,但它也不起作用 . 我的 sites-availablesites-enabled 代码是一样的 .

我究竟做错了什么?

3 回答

  • 0

    似乎server_name在转换为$ host变量时会选择server_name列表中的第一个 . 如果有效,请告诉我 . 我目前无法对此进行测试 .

    尝试将server_name交换为 server_name www.example.com example.com; 以及将 return 301 https://$host$request_uri; 更改为 return 301 https://$server_name$request_uri;

    server {
        server_name www.example.com example.com;
        return 301 https://$server_name$request_uri;
    }
    
    server {
        listen 443 ssl;
        # SSL CERT STUFF.
        server_name example.com;
        return 301 https://www.$server_name$request_uri;
    }
    
    server {
        listen 443 ssl;
        # SSL CERT STUFF.
        server_name www.example.com;
    
        # LOCATION STUFF
    }
    
  • 0

    这不是Nginx请求处理的有效配置 . 这很麻烦,你的if条件会在每次请求时被评估,而且我看不到你的非www到www的位置 .

    我拆分了http和https:

    server {
        listen 80 default_server;
        return 301 https://www.example.com$request_uri;
    }
    

    这是在单个重定向中处理的所有非https流量 . 现在为https:

    server {
        listen 443 default_server ssl;
        server_name www.example.com;
        root # should be outside location blocks ideally
        ......
    }
    

    默认服务器指令意味着此服务器将处理没有匹配服务器配置的任何请求 . 如果您不想这样,那么请在www.example.com之后添加example.com,而不是之前 . 此处结束的任何请求都将显示客户端浏览器栏中的第一个条目 .

    根据您的评论,您可能需要为其他域添加单独的块以避免SSL证书不匹配 . 试试这个:

    server {
        listen 443 ssl;
        server_name example.com;
        ssl_certificate .....;
        ssl_certificate_key .....;
        return https://www.example.com$request_uri;
    }
    
  • 1

    虽然OP已接受其中一个答案作为解决方案,但我只想指出它可能不是最佳做法 .

    正确的方法是使用 $host 而不是 $server_name (根据Mitchell Walls的例子)或硬编码的 www.exmple.com (根据miknik的例子) . 两者都产生了额外的443服务器指令,这是不必要和杂乱的 .

    server {
        listen 80 default_server;
        server_name www.example.com example.com;
        root /var/www/html;    # define your root directory here
        return 301 https://$host$request_uri;
    }
    
    server {
        listen 443 ssl;
        # SSL CERT STUFF.
        #server_name www.example.com;    you don't need to specify again here
    
        # LOCATION STUFF
    }
    

    $host$server_name 之间存在差异:

    • $host 包含"in this order of precedence: host name from the request line, or host name from the 'Host' request header field, or the server name matching a request" .

    • $server_name 包含处理请求的虚拟主机的server_name,因为它是在nginx配置中定义的 . 如果服务器包含多个server_names,则此变量中只存在第一个server_names .

相关问题