Nginx下HTTP强制重定向至HTTPS

对于nginx来说,配置http强制重定向至https有多种多样的写法。可以直接rewrite,也可以用301重定向。但是直接拷贝网上的配置往往会出现问题,所以应该考虑网站具体的配置情况。

首先,从官方文档来看,对整个域名通过正则匹配进行rewrite,然后以此来重定向至https的链接的方法官方是不推荐的(可见 http://wiki.nginx.org/Pitfalls#Taxing_Rewrites)。那么首先重定向的语句就应该写成:

return 301 https://$server_name$request_uri;

接着是重定向的语句应该放在哪里的问题。

对于编译安装的nginx,如果在编译的时候没有修改 --prefix 选项,配置文件会在 /usr/local/nginx/conf/nginx.conf 。其中默认包含了两个server段,一个是开启的,监听80端口;另一个被注释掉了,监听443端口(也即TLS所需的端口)。这两个端口分别使用各自的server段。也就是说只要将80端口server段下的站点location设置复制到443端口server段下面,这样就可以直接在80端口server段里面添加重定向语句以达到重定向的目的。

对于其他方式安装的nginx,或者自行修改过nginx.conf,那么可能不包括443端口server段。这时候可以选择像编译安装的默认配置一样,另起一个监听443端口的server段。但是如果觉得两个 server 段过于冗长,这时候的另一种做法是在一个server段里面同时监听80和443端口。如果这时候在这个server段里面直接添加重定向语句,就会使得http/https访问都会重定向到https。看似没有问题,但是仔细考虑一下就会发现,无论是http还是https都会无限地重定向下去。事实证明,在这种情况下Chrome会报“重定向循环”的错误。

那么明确问题之后,解决的方案就是只在访问http时重定向。配置可以这么写:

if ( $scheme = http ){
    return 301 https://$server_name$request_uri;
}

这样就可以解决这一问题。

但是实际上,官方的文档中不推荐在文档中使用 if 语句(If Is Evil),所以最好的办法还是使用两个 server 段(一个监听80,另一个监听443)来解决问题。

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

server {
    listen 443;
    ...
}

《Nginx下HTTP强制重定向至HTTPS》上有8条评论

  1. 找了很多方法,终于找到这个不会循环重定向的方法,但是就是我有二级域名的话,输入二级域名他也会自动跳到根域名去,这该怎么解决呢?

    1. $server_name应该会匹配到当前server段的server_name,所以可能和你的二级域名的配置有关系。
      另外,以我个人的经验来看,现在在生产环境上,基本上都是把监听80和443的server段给分开来写,直接用第一种return的方法会更好,因为nginx配置文件中一定要尽量避免使用if语句(If Is Evil)。

    1. 从 HTTP 到 HTTPS 的劫持问题业界目前已经有 HSTS (HTTP Strict Transport Security)的方案,具体实践上有两种做法:
      (1)可以考虑在 Nginx 的配置文件中添加 HSTS 响应头,例如

      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
      

      相当于告诉浏览器在一定时间内对该域名以及所有子域名在访问时强制 HTTPS;
      (2)手动提交自己的域名到浏览器的 HSTS Preload List 里面来使浏览器强制使用 HTTPS 进行访问。可以在这里进行提交。

      参考资料:
      https://zh.wikipedia.org/wiki/HTTP%E4%B8%A5%E6%A0%BC%E4%BC%A0%E8%BE%93%E5%AE%89%E5%85%A8
      https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注