Nginx 重定向(非 www 到 www)不适用于 Certbot

Posted

技术标签:

【中文标题】Nginx 重定向(非 www 到 www)不适用于 Certbot【英文标题】:Nginx redirect (non-www to www) not working with Certbot 【发布时间】:2018-12-12 01:04:40 【问题描述】:

我有一个运行 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


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

我做错了什么?

【问题讨论】:

“不工作”是什么意思? 我收到“错误请求 (400)”错误。我将编辑原始评论以包含它。 【参考方案1】:

似乎 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

【讨论】:

这是我的复制和粘贴错误。实际的服务器代码有一个右括号。现在正在编辑问题... 我测试了上面的更改,它似乎有效。事实上,这很可能不会将 https://example.com 转换为 https://www.example.com。这应该可以让你开始。永远记得尝试使用清除缓存的浏览器。 你的解决方案解决了一半的问题! http://example.com 现在可以正确重定向。但是,https://example.com 仍然返回错误请求 (400) 错误。您对如何使 https 非 www 到 www 重定向工作有任何建议吗? 好的,我又更新了。唯一可能不工作的步骤可能是第二次返回 301 https://www.$server_name$request_uri;可能必须更改为 www.example.com 有效!我选择了https://www.$server_name$request_uri。谢谢!【参考方案2】:

这不是 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,而不是在它之前。任何在此处结束的请求都将在客户端浏览器栏中显示第一个条目。

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

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

【讨论】:

感谢您的意见!您的代码给出的结果与 Mitchell Walls 的解决方案相同:http://example.com 现在可以正确重定向,但https://example.com 仍然返回错误请求 (400) 错误。关于为什么会发生这种情况的任何想法? 您是否有一个 SSL 证书同时打开了 example.com 和 www.example.com,或者 2 个证书? 我想我有一个 SSL 证书,上面有两个域。 我用另一个服务器块编辑了我的答案供您尝试添加,如果您使用 chrome 访问该站点,请单击地址栏中的挂锁,单击证书,详细信息,然后选择主题备用名称,如果将列出所有证书涵盖的域 感谢您的提示!我使用 Chrome 检查了 SSL,它是一个涵盖两者的证书。我最终使用了 Mitchell Walls 的编辑解决方案,它与您的非常相似。就像你说的那样,我为另一个域创建了一个单独的块,但是当我将 default_server 用于前 2 个块时,仍然发生相同的错误。当我忽略它并使用 Mitchell Walls 的代码时,它运行良好。【参考方案3】:

虽然 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,则此变量中只会出现第一个。

【讨论】:

关于主机的东西太棒了。只是对您的示例感到好奇,您如何为 example.com 和 www.example.com 定义 ssl 证书。 OP 有两个不同的证书。那么使用 https 直接访问 example.com 时,您的示例不会出错吗?

以上是关于Nginx 重定向(非 www 到 www)不适用于 Certbot的主要内容,如果未能解决你的问题,请参考以下文章

将 https 非 www 重定向到 https www 不工作的虚拟主机

非 www 到 www .htaccess 重定向不起作用? [复制]

htaccess - 将所有非 www 流量重定向到 www

将http非www重定向到https非www

NGINX 将 http 重定向到 https,将非 www 重定向到 ww

Nginx:将非www重定向到www https