it-swarm.cn

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

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

服务器{
收听80; 
 
位置/ {
重写^(。*)https://mysite.com$1永久; 
} 
 ... 


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

517
MikeN

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; 

       [....]
}
758
Saif Bechan

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

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

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

下面是前一个答案,由于正则表达式效率低下,简单的301非常好,如@kmindi

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

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

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

280
Michael Neale

我认为最好和唯一的方法应该是使用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套接字。将其更改为listen 80如果您不想要IPv6或想绑定其他地方。

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

来自Michael Neale的解决方案很好,但是根据pitfails的说法,重定向301是一个更好的解决方案;)

125
kmindi

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

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

上面的方法不适用于始终创建新的子域的情况。例如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
Aleck Landgraf

很久很久以前,我就正确答案发表了评论,但有一个非常重要的更正,但我觉得有必要在自己的答案中突出此更正。 如果您在任何时候都设置了不安全的HTTP并期望用户内容,具有表单,托管API或已配置了要与之对话的任何网站,工具,应用程序或实用程序,则上述任何答案都不能安全使用您的网站。

当对您的服务器发出POST请求时,会发生此问题。如果服务器响应以普通30x redirect POST内容将丢失。发生的情况是浏览器/客户端会将请求升级到SSL,但是降级了POSTGET请求。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
Mahmoud Al-Qudsi

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

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

我设法做到这一点:

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
stamster

如果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声明中的yo内容。但是,如果您在那里有多个子域或使用通配符,则将不起作用,因为$server_name仅在server_name声明后使用first条目,更重要的是,回显通配符(不扩展它)。

那么如何在保持安全性的同时支持多个域呢?在我自己的系统上,我通过first处理了这个难题,列出了一个不使用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块匹配none合法域名,并提供令人反感的服务。 。 。 。

[1]从技术上讲,“ proxy”是错误的Word,因为您的服务器不会关闭并满足客户端的请求,只是发送重定向,但是我不确定正确的Word是什么。我也不确定目标是什么,但是它充满了噪音,消耗了CPU和带宽,因此您最好停止它。

1
Paul A Jungwirth

似乎没人真的100%正确地做到了。要使端口80的请求到达整个Web服务器的443当量,您需要使用listen指令,而不是使用server_name指令来指定通用名称。另请参阅 https://nginx.org/en/docs/http/request_processing.html

服务器{
默认监听80; 
默认监听[::]:80; 
返回307 https:// $ Host $ request_uri; 
} 
  • $ Host捕获子域名。
  • 307和308都包括POST和GET请求URI。
  • 307是临时的,经过全面测试后更改为永久308:

并确保检查/etc/nginx/conf.d/中已经存在的内容,因为我经常会遇到default.conf返回一些现有vhost的问题。我处理nginx问题的顺序始终是从移出默认文件开始,然后将其逐行注释掉,以查看出错的地方。

0
Julius