我想将Web服务器上的所有http请求重写为https请求,我从以下内容开始:
服务器{ 听80; 位置 / { 重写^(。*)https://mysite.com$1永久; } ...
一个问题是,这剥夺了任何子域信息(例如,node1.mysite.com / folder),我该如何重写以上内容以将所有内容重新路由到https并维护该子域?
我想将Web服务器上的所有http请求重写为https请求,我从以下内容开始:
服务器{ 听80; 位置 / { 重写^(。*)https://mysite.com$1永久; } ...
一个问题是,这剥夺了任何子域信息(例如,node1.mysite.com / folder),我该如何重写以上内容以将所有内容重新路由到https并维护该子域?
Answers:
事实证明,我对这个问题的第一个答案在某些时候是正确的,但它又变成了另一个陷阱-要保持最新状态,请检查对改写陷阱进行纳税
许多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;
[....]
}
$host
,$server_name
则必须使用代替。
注意: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;
}
向客户端发送永久重定向。
我认为最好和唯一的方法应该是使用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是一个更好的解决方案;)
在服务器块内,您还可以执行以下操作:
# Force HTTPS connection. This rules is domain agnostic
if ($scheme != "https") {
rewrite ^ https://$host$uri permanent;
}
上面的方法不适用于始终创建新的子域的情况。例如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
# ...
}
301 https://*/
在这里的其他答案中,nginx会过早返回或取消请求。server_name _;
同$host
是奏效了答案。+1
_
为实际的域,例如,.domain.com
我有两台服务器,而nginx意外将我的一台服务器定向到默认服务器。
很久很久以前,我就正确答案发表了评论,但有一个非常重要的更正,但是我觉得有必要在自己的答案中突出此更正。如果您在任何时候都设置了不安全的HTTP并期望用户内容,具有表单,托管API或配置了任何网站,工具,应用程序或实用程序来与您的站点对话,则上述任何答案都不能安全使用。
当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";
[....]
}
我设法做到这一点:
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;
}
}
如果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]从技术上讲,“ proxy”是一个错误的词,因为您的服务器不会关闭并满足客户的请求,只是发送重定向,但我不确定哪个词是正确的。我也不确定目标是什么,但是它充满了噪音,消耗了CPU和带宽,因此您最好停止它。
似乎没人真的100%正确地做到了。要使端口80的请求到达整个Web服务器的443个等效端口,您需要使用listen指令,而不是server_name指令来指定全部捕获名称。另请参阅https://nginx.org/en/docs/http/request_processing.html
服务器{ 默认听80; 听默认[::]:80; 返回307 https:// $ host $ request_uri; }
并确保检查/etc/nginx/conf.d/中已经存在的内容,因为我经常会遇到default.conf返回一些现有vhost的问题。我处理nginx问题的顺序始终是从移出默认文件开始,然后将其逐行注释掉,以查看出错的地方。