【问题标题】:How do I redirect HTTPS to HTTP on NGINX?如何在 NGINX 上将 HTTPS 重定向到 HTTP?
【发布时间】:2011-04-23 01:43:07
【问题描述】:

有没有办法通过在域的 vhost 文件中添加规则来将 HTTPS 请求重定向到 HTTP?

【问题讨论】:

    标签: ssl nginx redirect https


    【解决方案1】:

    为什么这样的东西有用?乍一看,我不确定它是否可以完成。但它提出了一个有趣的问题。

    您可以尝试在配置文件中添加重定向语句并重新启动服务器。可能会发生两种可能性:

    1. 服务器将发出重定向 - 您似乎想要的。
    2. 服务器会先做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。相关问题:stackoverflow.com/questions/3470290/…
    • server_name _ 应该在 nginx 版本 > 0.6.25 中使用而不是 server_name _ *
    【解决方案2】:

    rewriteif 应避免使用 Nginx。著名的一句话是,“Nginx 不是 Apache”:换句话说,Nginx 处理 URL 的方法比重写更好。 return 在技术上仍然是重写模块的一部分,但它没有 rewrite 的开销,并且不像 if 那样需要注意。

    Nginx 在why if is "evil" 上有一个完整的页面。它还提供了一个建设性的页面来解释why rewrite and if are bad,以及如何解决它。以下是该页面关于rewriteif 的内容:

    这是一种错误、繁琐且无效的方式。

    您可以使用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。

            【讨论】:

              猜你喜欢
              • 2022-08-14
              • 2011-03-29
              • 2018-01-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-10-11
              相关资源
              最近更新 更多