如何将https://请求重定向到NGINX中的http://?

时间:2010-10-08 19:49:08

标签: ssl redirect nginx

有没有办法通过在域的vhost文件中添加规则将https://请求重定向到http://?我正在使用NGINX。

7 个答案:

答案 0 :(得分:33)

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

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

  1. 服务器将发出重定向 - 您似乎想要的内容。
  2. 服务器将首先进行https交换,然后发出重定向,在这种情况下,重点是什么?
  3. 如果我想出更具体的东西,会增加更多。

    更新: (几个小时后) 你可以试试这个。您需要将它放在 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版本>中应使用server_name _代替server_name _ *。 0.6.25(感谢@Luca Steeb)

答案 1 :(得分:32)

Nginx应该避免

rewriteif。着名的一句是“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标头,只要您坚持使用端口80和443,就可以使用$host代替$http_host。否则,您需要动态填充$http_host替代品。尽管使用server,但只要该代码显示在location的根目录中(而不是if块中),此代码就是高效且安全的。但是,您需要使用默认服务器才能适用,应使用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;
    }
}

答案 2 :(得分:6)

location / {
    if ($scheme = https) {
        rewrite ^(.*)? http://$http_host$1 permanent;
    }
}

答案 3 :(得分: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

答案 4 :(得分:0)

唯一简单的规则已在我上面的帖子中解释过:

server {
    listen ip:443;
    server_name www.example.com;
    rewrite ^(.*) http://$host$1 permanent;
}

答案 5 :(得分:0)

这对我有所帮助:

<div class="view">
  <ul class="list">
    <li class="frst">
      <img src="http://www.guessthelogo.com/wp-content/uploads/2012/11/random-dice.gif" />
    </li>
    <li class="scnd">
      <img src="https://avatars.yandex.net/get-music-content/a19fc9b4.a.1767585-1/200x200" />
    </li>
    <li class="thrd">
      <img src="http://randomacts.channel4.com/images/fb_logo.gif" />
    </li>
    <li class="frth">
      <img src="http://cs620120.vk.me/v620120530/93f0/k7U9HGQOBkw.jpg" />
    </li>
  </ul>
  <div class="ctrl">
    <div class="bullet one"></div>
    <div class="bullet two"></div>
    <div class="bullet three"></div>
    <div class="bullet four"></div>
  </div>
</div>

$(".list li:gt(0)").hide();

var int = setInterval(function(){
$('.list > :first-child')
.fadeOut()
.next()
.fadeIn()
.end()
.appendTo('.list');} ,3000);

$(".bullet").on("click", function(){
  clearInterval(int);
  $(".list li").fadeOut();

  var $this = $(this);

  if($this.hasClass("one")){
    $(".list li.frst").fadeIn();
  }else if($this.hasClass("two")){
    $(".list li.scnd").fadeIn();
  }else if($this.hasClass("three")){
    $(".list li.thrd").fadeIn();
  }else if($this.hasClass("four")){
    $(".list li.frth").fadeIn();
  }
})

答案 6 :(得分:0)

不是一个合适的解决方案,但我能够通过使用 Cloudflare 解决我的用例,它为我透明地处理了 SSL。