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

时间:2018-07-03 16:30:06

标签: django ssl nginx https url-redirection

我有一个运行Python / Django / uWSGI / Nginx设置的网站。我还使用Certbot在我的网站上启用https。我从非www重定向到www(例如,从“ example.com”重定向到“ www.example.com”)导致出现“错误请求(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 :(得分:1)

这不是用于Nginx请求处理的有效配置。太乱了,您的条件是否会在每次请求中得到评估,而且我什至看不到您的非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 :(得分: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
}

答案 2 :(得分:0)

尽管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包含“按此优先顺序:请求行中的主机名,或“主机”请求标头字段中的主机名,或与请求匹配的服务器名。”
  • $server_name包含处理请求的虚拟主机的server_name,如nginx配置中所定义。如果服务器包含多个server_name,则此变量中仅出现第一个。