题 在Nginx中,如何在维护子域的同时将所有http请求重写为https?


我想将我的Web服务器上的所有http请求重写为https请求,我开始使用以下内容:

服务器{
    听80;

    地点 / {
      重写^(。*)https://mysite.com$1永久;
    }
...


一个问题是,这剥夺了任何子域信息(例如,node1.mysite.com/folder),我怎么能重写上面的内容以将所有内容重新路由到https并维护子域?


495
2017-09-21 14:04




请考虑将“已接受的答案”移至 serverfault.com/a/171238/90758。那是正确的。 - olafure
只需使用$ server_name而不是硬编码的mysite.com - Fedir RYKHTIK


答案:


在新版本的nginx中正确的方式

结果我的第一个回答这个问题在某个时间是正确的,但它变成了另一个陷阱 - 保持最新请检查 对重写陷阱征税

我被许多SE用户纠正过,所以归功于他们,但更重要的是,这里是正确的代码:

server {
       listen         80;
       server_name    my.domain.com;
       return         301 https://$server_name$request_uri;
}

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000" always; 

       [....]
}

737
2017-12-05 20:43



您必须逐个域地执行此操作 - 不是吗?如果您想将它应用于服务器上的每个域,该怎么办? - JM4
@ JM4:如果在重写中使用$ host $而不是server_name,并将default_server添加到listen指令,它将适用于服务器上的每个域。 - Klaas van Schelven
重要的是要提到301存储在本地缓存中而没有到期日期。配置更改时不是很有用 - Trefex
@everyone使用307重定向来保留POST内容。 - Mahmoud Al-Qudsi
请注意,您必须使用 $host 代替 $server_name 如果您正在使用子域名。 - Catfish


注意:最好的方法是提供 https://serverfault.com/a/401632/3641  - 但在这里重复:

server {
    listen         80;
    return 301 https://$host$request_uri;
}

在最简单的情况下,您的主机将被修复为您要发送给他们的服务 - 这将执行301重定向到浏览器,浏览器URL将相应更新。

下面是前面的答案,由于正则表达式效率低,如@kmindi所示,简单的301很好

我一直在使用nginx 0.8.39及以上版本,并使用以下内容:

 server {
       listen 80;
       rewrite ^(.*) https://$host$1 permanent;
 }

向客户端发送永久重定向。


269
2017-08-17 03:07



我认为它应该是80 - 因为这是在监听http然后告诉客户回来作为https(443)。 - Michael Neale
这应该是最好的答案! - Nathan
这是最讨人喜欢的答案。 - Case
这是最简单的,但安全性最低 - 这样您就可以让服务器将用户重定向到任何页面,而无需检查是否允许在您的服务器上使用它。如果您的服务器为mydomain.co服务,恶意用户仍然可以使用您的服务器将用户重定向到其他域,例如google.com。 - friedkiwi
@ cab0lt这里没有安全问题。提供重定向不会带来安全风险。如果存在访问控制要求,则应在浏览器请求新URL时检查这些要求。浏览器不会仅根据重定向获取访问权限,也不需要重定向来请求新URL。 - mc0e


我认为最好也是唯一的方法应该是使用a HTTP 301永久移动 像这样重定向:

server {
    listen         [::]:80;
    return 301 https://$host$request_uri;
}

HTTP 301永久移动 根据已经提到的,重定向也是最有效的,因为没有要评估的正则表达式 pitfails


新的 HTTP 308永久移动 保留Request方法并且是 主流浏览器支持。例如,使用 308 阻止浏览器更改请求方法 POST 至 GET 用于重定向请求。


如果你想 保留主机名和子域 这就是方法。

这个 如果你没有DNS,它仍然有效,因为我也在本地使用它。 我要求举例 http://192.168.0.100/index.php 并将完全重定向到 https://192.168.0.100/index.php

我用 listen [::]:80 在我的主人,因为我有 bindv6only 调成 false,所以它也绑定到ipv4 socket。改为 listen 80 如果您不想要IPv6或想要绑定到其他地方。

Saif Bechan的解决方案使用了 server_name 在我的情况下是localhost但是无法通过网络访问。

Michael Neale的解决方案很好,但根据pitfails,有一个更好的解决方案,重定向301;)


121
2018-06-23 17:19



很好,你尝试引用它,但301不适用于HTTPS。 - Case
什么不起作用?声明的服务器部分用于将未加密的http(无s)流量永久重定向到加密服务器(未列出侦听443(https)的部分) - kmindi
我用https和所有东西检查了这个效果很好 - @kmindi我更新了我的答案,参考你的 - 我认为这是正确的方式,这一直在弹出!干得好。 - Michael Neale
使用域(非ip)请求时,除非我将'[::]:80'更改为'80',否则不起作用。 - Joseph Lust
这可能是预期的行为: trac.nginx.org/nginx/ticket/345。我更新了答案来描述listen选项。 - kmindi


以上不适用于随时创建新子域。 例如AAA.example.com BBB.example.com,用于约30个子域。

最后得到一个配置使用以下内容:

server {
  listen 80;
  server_name _;
  rewrite ^ https://$host$request_uri? permanent;
}
server {
  listen  443;
  server_name example.com;
  ssl on;
  ssl_certificate /etc/ssl/certs/myssl.crt;
  ssl_certificate_key /etc/ssl/private/myssl.key;
  ssl_prefer_server_ciphers       on;
# ...
# rest of config here
# ...
}

17
2018-06-25 04:29



谢谢! nginx要么返回 301 https://*/ 或者在此处的其他答案中提前取消请求。 server_name _; 同 $host 是诀窍的答案。 +1 - zamnuts
这个是最佳的!但是,我建议一些人来替换 _ 与实际域名,例如 .domain.com 我有两台服务器,nginx意外地将我的一台服务器指向默认服务器。 - zzz
这是唯一对我有用的答案,谢谢! - Snowman
非常感谢伙计..我尝试了许多解决方案,但没有成功。这个解决方案很棒,对我有用。服务器名称 _;这意味着什么......我不明白。请解释一下这个。 - Pavan Kumar


在服务器块中,您还可以执行以下操作:

# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
}

16
2017-07-31 19:50



此配置导致我的服务器生成重定向循环 - Corkscreewe
可能导致另一个重定向到位或者您的网站/应用中未启用https - Oriol
除了这一个之外,其他人似乎都没有工作。使用nginx版本:nginx / 1.10.0(Ubuntu) - ThatGuy343
upvoted for https:// $ host $ uri - AMB
如果您在负载均衡器后面,这是要走的路! - Antwan


很久很久以前,我发表了对正确答案的评论并进行了非常重要的修正,但我觉得有必要在自己的答案中强调这一修正。 如果您在任何时候使用不安全的HTTP设置并期望用户内容,拥有表单,托管API或配置任何网站,工具,应用程序或实用程序来与您的站点通信,则以前的答案都不是安全的。

当a时出现问题 POST 请求发送到您的服务器。如果服务器响应平淡 30x 重定向 POST内容将丢失。 会发生什么是浏览器/客户端将请求升级到SSL但是 下坡 该 POST 到了 GET 请求。该 POST 参数将丢失,并且将向您的服务器发出不正确的请求。

解决方案很简单。你需要使用一个 HTTP 1.1 307 重定向。这在RFC 7231 S6.4.7中有详细说明:

  Note: This status code is similar to 302 (Found), except that it
  does not allow changing the request method from POST to GET.  This
  specification defines no equivalent counterpart for 301 (Moved
  Permanently) ([RFC7238], however, defines the status code 308
  (Permanent Redirect) for this purpose).

根据公认的解决方案改编的解决方案是使用 307 在您的重定向代码中:

server {
       listen         80;
       server_name    my.domain.com;
       return         307 https://$server_name$request_uri;
}

server {
       listen         443 ssl;
       server_name    my.domain.com;
       # add Strict-Transport-Security to prevent man in the middle attacks
       add_header Strict-Transport-Security "max-age=31536000"; 

       [....]
}

6
2018-03-19 20:24



非常非常非常非常满意的命令。谢谢! - Denis Matafonov


我设法做到这样:

server {
listen 80;
listen 443 ssl;

server_name domain.tld www.domain.tld;

# global HTTP handler
if ($scheme = http) {
        return 301 https://www.domain.tld$request_uri;
}

# global non-WWW HTTPS handler
if ($http_host = domain.tld){
        return 303 https://www.domain.tld$request_uri;
}
}

https://stackoverflow.com/a/36777526/6076984


4
2018-04-21 18:27



更新版本,没有IF: paste.debian.net/plain/899679 - stamster


我在AWS ELB后面运行ngnix。 ELB正在通过http与ngnix交谈。由于ELB无法向客户端发送重定向,因此我检查X-Forwarded-Proto标头并重定向:

if ($http_x_forwarded_proto != 'https') {
    return 301 "https://www.exampl.com";
}

3
2018-01-04 17:09





如果你 return 301 https://$host$request_uri; 作为端口80上的默认响应,您的服务器可能迟早会进入开放代理列表[1],并开始被滥用以在Internet上的其他位置发送流量。如果您的日志填满了这样的消息,那么您知道它发生在您身上:

42.232.104.114 - - [25/Mar/2018:04:50:49 +0000] "GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1" 301 185 "http://www.ioffer.com/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Hotbar 4.1.8.0; RogueCleaner; Alexa Toolbar)"

问题是 $host 将回显浏览器发送的内容 Host 标题甚至来自HTTP开头行的主机名,如下所示:

GET http://www.ioffer.com/i/new-fashion-fine-gold-bracelet-versaec-bracelet-641175733 HTTP/1.1

由于这个问题,这里的一些其他答案建议使用 $server_name 代替 $host$server_name 总是评估什么  投入 server_name 宣言。但是如果你有多个子域或使用通配符,那将无法工作,因为 $server_name 只使用 第一 进入之后 server_name 声明,更重要的是只回显一个通配符(不扩展它)。

那么如何在保持安全性的同时支持多个域?在我自己的系统上,我已经解决了这个难题 第一 列出一个 default_server 不使用的块 $host,然后列出一个通配符块:

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

(您还可以在第二个块中列出多个域。)

通过这种组合,无与伦比的域将被重定向到硬编码的地方(总是如此) example.com),与您自己匹配的域名将转到正确的位置。您的服务器将无法用作开放代理,因此您不会遇到麻烦。

如果你感觉很恶心,我想你也可以做到 default_server 块匹配 没有 您的合法域名并提供令人反感的内容。 。 。 。

[1]从技术上讲,“代理”是错误的,因为你的服务器没有出去并满足客户端的请求,只是发送重定向,但我不确定正确的单词是什么。我也不确定目标是什么,但它会用噪音填满你的日志并消耗你的CPU和带宽,所以你不妨停下来。


0
2018-03-25 05:58