如何在 NGINX 上将 HTTPS 重定向到 HTTP?
Posted
技术标签:
【中文标题】如何在 NGINX 上将 HTTPS 重定向到 HTTP?【英文标题】:How do I redirect HTTPS to HTTP on NGINX? 【发布时间】:2011-04-23 01:43:07 【问题描述】:有没有办法通过在域的 vhost 文件中添加规则来将 HTTPS 请求重定向到 HTTP?
【问题讨论】:
【参考方案1】:为什么这样的东西有用?乍一看,我不确定它是否可以完成。但它提出了一个有趣的问题。
您可以尝试在配置文件中添加重定向语句并重新启动服务器。可能会发生两种可能性:
-
服务器将发出重定向 - 您似乎想要的。
服务器会先做https交换,然后再发出重定向,这样的话,有什么意义呢?
如果我想出更具体的内容,会添加更多内容。
更新: (几个小时后) 你可以试试这个。你需要把它放在你的 nginx.conf 文件中 -
server
listen 443;
server_name _ *;
rewrite ^(.*) http://$host$1 permanent;
向客户端发送永久重定向。我假设您使用端口 443(默认)作为 https。
server
listen 80;
server_name _ *;
...
添加此项,以便您在端口 80 上的正常 http 请求不受干扰。
更新: 2016 年 12 月 18 日
- 在 nginx 版本 > 0.6.25 中应使用 server_name _
而不是 server_name _ *
(感谢 @Luca Steeb)
【讨论】:
旧答案,但只想说它很有用,因为现在有“不安全的内容”规则。例如,使用 iframe 时。 它有用的另一个原因:我有一个托管多个域的服务器。其中一个具有 SSL,但其他域没有 SSL 证书。当有人通过 HTTPS 访问其他域时,它需要重定向到 HTTP 这对我不起作用,我在访问我的服务器时仍然遇到问题:OpenSSL: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol Unable to establish SSL connection.
我认为解决方案不对。在没有ssl on
的情况下将 443 重定向到 80,与 https 不匹配,它不应该工作。但是如果添加ssl on
,则需要ssl_certificates,并且必须提供正确的证书。所以我认为它不能在没有证书的情况下将 https 重定向到 http。相关问题:***.com/questions/3470290/…
server_name _
应该在 nginx 版本 > 0.6.25 中使用而不是 server_name _ *
【参考方案2】:
rewrite
和 if
应避免使用 Nginx。著名的一句话是,“Nginx 不是 Apache”:换句话说,Nginx 处理 URL 的方法比重写更好。 return
在技术上仍然是重写模块的一部分,但它没有 rewrite
的开销,并且不像 if
那样需要注意。
Nginx 在why if
is "evil" 上有一个完整的页面。它还提供了一个建设性的页面来解释why rewrite
and if
are bad,以及如何解决它。以下是该页面关于rewrite
和if
的内容:
这是一种错误、繁琐且无效的方式。
您可以使用return
正确解决此问题:
server
listen 443 ssl;
# You will need a wildcard certificate if you want to specify multiple
# hostnames here.
server_name domain.example www.domain.example;
# If you have a certificate that is shared among several servers, you
# can move these outside the `server` block.
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/cert.key;
# 301 indicates a permanent redirect. If your redirect is
# temporary, you can change it to 302 or omit the number
# altogether.
# $http_host is the hostname and, if applicable, port--unlike $host,
# which will break on non-standard ports
# $request_uri is the raw URI requested by the client, including any
# querystring
return 301 http://$http_host$request_uri;
如果您预计很多机器人不会发送 Host
标头,则可以使用 $host
而不是 $http_host
,只要您坚持使用端口 80 和 443。否则,您需要动态填充$http_host
替代品。尽管使用了if
,只要它出现在server
的根(而不是location
块)中,这段代码就是高效且安全的。但是,您需要使用默认服务器才能适用,这应该避免使用 https。
set $request_host $server_name:$server_port;
if ($http_host)
set $request_host $http_host;
如果您想对特定路径强制执行 SSL/TLS,但否则禁止:
server
listen 443 ssl;
server_name domain.example;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/cert.key;
location /
return 301 http://$host$request_uri;
location /secure/
try_files $uri =404;
server
listen 80;
server_name domain.example;
location /
try_files $uri =404;
location /secure/
return 301 https://$http_host$request_uri;
如果您的服务器没有与客户端直接通信(例如,如果您使用 CloudFlare),事情会变得有点复杂。您需要确保与客户端直接通信的任何服务器都在请求中添加适当的 X-Forwarded-Proto
标头。
使用这是一个混乱的提议;有关完整说明,请参阅IfIsEvil。为了使其有用,if
块不能位于 location
块内,原因有多种。这会强制使用rewrite
进行URI 测试。 简而言之,如果您必须在生产服务器上使用它...不要。可以这样想:如果您已经超越了 Apache,那么您就已经超越了这个解决方案。
/secure、/secure/ 和 /secure/ 中的任何内容都将强制执行 https,而所有其他 URI 将强制执行 http。 (?! )
PCRE 构造是negative lookahead assertion。 (?: )
是 non-capturing group。
server
# If you're using https between servers, you'll need to modify the listen
# block and ensure that proper ssl_* statements are either present or
# inherited.
listen 80;
server_name domain.example;
if ($http_x_forwarded_proto = https)
rewrite ^(?!/secure)/ http://$http_host$request_uri? permanent;
if ($http_x_forwarded_proto != https)
rewrite ^/secure(?:/|$) https://$http_host$request_uri? permanent;
【讨论】:
好的,所以如果我理解正确的话,如果没有先建立 https 连接,就无法通过 http 向请求 https 的客户端提供服务。所以如果你想把人们从 https 转移到 http,你需要证书吗? @Freek 对,尽管您应该始终支持 HTTPS;这是一个非常基本但有效的安全功能,任何关心密码的用户都会想要它。您可以从Let's Encrypt 获得免费证书。即使您的网站不包含任何敏感信息,它也可能被用作攻击媒介。 如果您使用“删除 s”(https -> http)的弹性负载均衡器,则唯一可行的解决方案是使用 if ($http_x_forwarded_proto = https)... @jmcollin92 理想情况下,您应该在 ELB 和 Nginx 之间使用 HTTPS。这就是我倾向于做的事情。它不太方便,但更安全。 @jmcollin92 如果安全性是最重要的,那么麻烦是值得的。这使得可以访问您的 VPC 中的服务器的黑客更难以获取有用的信息。黑客通常会瞄准被遗忘的旧服务器,然后通过网络进行攻击。【参考方案3】:location /
if ($scheme = https)
rewrite ^(.*)? http://$http_host$1 permanent;
【讨论】:
如果您想确保所有内容都匹配,可能需要添加一个比 / 更具包容性的位置标志。但老实说,服务器的答案可能是最好的。你是如何处理虚拟主机的?【参考方案4】:这个问题更适合 serverfault.com 网站。
重定向到 http 的更好方法:
server
listen 443;
return 301 http://$host$request_uri;
这避免了重写中的“if”子句和正则表达式,它们是迄今为止其他解决方案的特征。两者都对性能有影响,但在实践中,您必须拥有相当多的流量才有意义。
根据您的设置,您可能还想在 listen 子句中指定一个 ip,或许还需要在上面的 servername 子句中指定一个。照原样,它将适用于所有域名的所有端口 443 请求。您通常希望每个域都有一个带有 https 的 IP,因此大多数情况下将上述内容与 IP 绑定比将其与域名绑定更重要,但也有一些变化,例如所有域都是一个域的子域。
编辑:TLS 现在已接近通用,并且使用它的服务器名称识别 (SNI),它允许多个域上的 HTTPS 站点共享一个 IP。有一篇很好的文章here
【讨论】:
【参考方案5】:这对我有帮助:
server
listen 443;
server_name server.org www.server.org;
rewrite ^ http://$server_name$request_uri? permanent;
【讨论】:
【参考方案6】:不是一个合适的解决方案,但我能够通过使用 Cloudflare 解决我的用例,它透明地为我处理 SSL。
【讨论】:
【参考方案7】:我上面的帖子已经解释了唯一简单的规则:
server
listen ip:443;
server_name www.example.com;
rewrite ^(.*) http://$host$1 permanent;
【讨论】:
【参考方案8】:server
listen 80;
listen [::]:80;
server_name mywebsite.com ;
return 301 https://$host$request_uri;
插入此代码后,HTTP 默认服务器的所有流量都会重定向到 HTTPS。
【讨论】:
以上是关于如何在 NGINX 上将 HTTPS 重定向到 HTTP?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Node JS 上将 http 重定向到 https
如何在 WebSphere Application Server v9 上将 URL http 重定向到 https
如何在 ELB 应用程序负载均衡器上将 HTTPS 重定向到 HTTP
在 Flask+Heroku 上将 HTTP 重定向到 HTTPS