简体   繁体   English

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

[英]Nginx redirect (non-www to www) not working with Certbot

I have a website running with a Python/Django/uWSGI/Nginx setup. 我有一个运行Python / Django / uWSGI / Nginx设置的网站。 I also use Certbot to enable https on my site. 我还使用Certbot在我的网站上启用https。 My redirects from non-www to www (eg "example.com" to "www.example.com") result in a "Bad Request (400)" message even though I couldn't spot any deviations from the Nginx/Certbot documentation. 我从非www重定向到www(例如,从“ example.com”重定向到“ www.example.com”)导致出现“错误请求(400)”消息,即使我无法发现与Nginx / Certbot文档的任何差异。 Here is the relevant part of my sites-available Nginx code: 这是我的sites-available的相关部分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

}

I found a similar StackOverflow answer ( Nginx: redirect non-www to www on https ) but none of the solutions worked for me. 我找到了类似的StackOverflow答案( Nginx:将非www重定向到https上的www ),但是没有一种解决方案适合我。 I have SSL certificates for both example.com and www.example.com. 我同时拥有example.com和www.example.com的SSL证书。 I also tried creating a separate 443 ssl server block for example.com based on the comments in that answer but it didn't work either. 我还尝试根据该答案中的注释为example.com创建一个单独的443 ssl服务器块,但它也不起作用。 My sites-available and sites-enabled code is the same. 我的sites-available代码和sites-enabled代码是相同的。

What am I doing wrong? 我究竟做错了什么?

This is not an efficient configuration for Nginx request processing. 这不是用于Nginx请求处理的有效配置。 It's messy, your if condition gets evaluated on every request and I don't see where your non www to www is even meant to happen. 太乱了,您的条件是否会在每个请求中得到评估,而且我看不出您的非www到www打算在哪里发生。

I'd split http and https: 我将http和https分开:

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

Thats all non https traffic taken care of in a single redirect. 多数民众赞成在单个重定向中处理所有非https流量。 Now for the https: 现在使用https:

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

The default server directive means this server will handle any requests which do not have a matching server configuration. 默认服务器指令意味着该服务器将处理任何没有匹配的服务器配置的请求。 If you don't want that then add example.com after www.example.com, not before it. 如果您不希望这样做,请在www.example.com之后而不是之前添加example.com。 Any requests ending up here will display the first entry in the client browser bar. 到此为止的所有请求都将在客户端浏览器栏中显示第一个条目。

Based on your comments you might need to add a separate block for the other domain to avoid an SSL certificate mismatch. 根据您的评论,您可能需要为其他域添加一个单独的块,以避免SSL证书不匹配。 Try this: 尝试这个:

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

It seems that server_name when translated to the $host variable selects the first in the list of server_name. 似乎将server_name转换为$ host变量后,将选择server_name列表中的第一个。 Let me know if that works. 让我知道是否可行。 I can't quite test this currently. 我目前无法完全测试。

Try swapping server_name to server_name www.example.com example.com; 尝试将server_name交换为server_name www.example.com example.com; as well as changing return 301 https://$host$request_uri; 以及更改return 301 https://$host$request_uri; to return 301 https://$server_name$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
}

Although the OP has accept one of the answers as the solution, I just want to point out that it may not be the best practice. 尽管OP接受了答案之一作为解决方案,但我只想指出这可能不是最佳实践。

The correct way is to use $host instead of $server_name (as per Mitchell Walls' example) or hardcoded www.exmple.com (as per miknik's example). 正确的方法是使用$host而不是$server_name (按照Mitchell Walls的示例)或硬编码的www.exmple.com (按照miknik的示例)。 Both results an extra 443 server directive that is not necessary and messy. 两者都会导致多余的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
}

There is a difference between $host and $server_name : $host$server_name之间是有区别的:

  • $host contains "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". $host包含“按此优先顺序:请求行中的主机名,或'Host'请求标头字段中的主机名,或与请求匹配的服务器名”。
  • $server_name contains the server_name of the virtual host which processed the request, as it was defined in the nginx configuration. $server_name包含处理请求的虚拟主机的server_name,如在nginx配置中定义的那样。 If a server contains multiple server_names, only the first one will be present in this variable. 如果服务器包含多个server_name,则此变量中仅存在第一个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM