我在子域上有一个注册页面,例如:https://signup.example.com

它只能通过HTTPS访问,但我担心人们可能会通过HTTP偶然发现它并得到404。

我在nginx中的html / server块如下所示:

html {
  server {
    listen 443;
    server_name signup.example.com;

    ssl                        on;
    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    ssl_session_timeout 30m;

    location / {
      root /path/to/my/rails/app/public;
      index index.html;
        passenger_enabled on;
    }
  }
}


我可以添加些什么,以便那些去http://signup.example.com的人被重定向到https://signup.example.com? (仅供参考,我知道有些Rails插件可以强制使用SSL,但希望避免这种情况)

评论

在Nginx中可能重复,如何在维护子域的同时将所有http请求重写为https?

#1 楼

根据nginx陷阱,最好使用$request_uri省略不必要的捕获。在这种情况下,请添加问号以防止Nginx将任何查询参数加倍。

server {
    listen      80;
    server_name signup.mysite.com;
    rewrite     ^   https://$server_name$request_uri? permanent;
}


评论


或者,根据您链接的网站,“更好”:返回301 http://domain.com$request_uri;

– nh2
2012年8月14日下午4:56

一则评论。 $ server_name $获取第一个server_name变量。因此,如果您的配置中没有非FQN名称,请注意这一点

–engineerDave
2012年8月15日在15:48



@ nh2这是文档错误的另一种情况,因为使用return 301 ...会在重写方法实际起作用时导致“重定向过多”错误。

–迈克·伯大尼(Mike Bethany)
15年7月2日在12:06

现在已记录为“也很糟糕”。 @MikeBethany return 301确实可以正常工作,除非(我想)您正在通过同时侦听两个端口来触发它以获取正确的URL(配置示例触发问题:以serverfault.com/a/474345/29689的第一个答案为准,然后忽略如果)。

–布莱布莱德
15年8月3日在8:57

我想知道这些年来发生了什么变化以及其他答案是否更好:serverfault.com/a/337893/119666

–瑞安
18年7月14日在0:59

#2 楼

官方方法中描述的最佳方法是使用return指令:

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


评论


最短的答案,在我的情况下效果很好

–mateusz.fiolka
2012年9月8日在22:37

通常建议这样做,因为它会返回301永久移动(您的链接已永久移动)以及重写

– sgb
2014-12-17 17:17



即使您设置了proxy_set_header X-Forwarded-Proto https,它也不会导致“重定向过多”错误;

–迈克·伯大尼(Mike Bethany)
15年7月2日在12:03

@MikeBethany您是在定义监听443吗?在同一街区?

– Joe B
15年8月12日在18:06

这应该是公认的答案。

– sjas
16-09-13在10:08

#3 楼

如果要将它们全部保留在一个服务器块中,这是正确且最有效的方法:

这里是相同的,但是效率更高,通过仅在http协议上运行重写,它避免了必须在每个请求中检查$ scheme变量。但认真的说,这是一件很小的事情,您无需将它们分开。

server {
    listen   80;
    listen   [::]:80;
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

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


评论


太好了,即使这个答案是正确的,一些胆小鬼还是拒绝了这个答案,却没有说出原因。也许那些“如果是邪恶的”信徒中的另一个。如果您不愿阅读有关If的Nginx文档,您会知道IfIsNOTEvil,只是CERTAIN在location {}上下文中使用了它,我们在这里都不会做。我的回答绝对是正确的做事方式!

–DELETEDACC
13年2月26日在21:50



我没有对此表示拒绝,但我想指出的是,在最新版本中,默认值已更改为“ default_server”。

– spudder
13-10-22在3:32

如果第二种解决方案效率更高,则第一种解决方案可能不是最有效的。您甚至描述了为什么不应该在其中使用if:“它避免了每次请求都必须检查$ scheme变量”。不使用ifs的意义不仅在于性能,还在于声明性而非强制性。

– pepkin88
16-10-24在11:12



+1代表if($ scheme = http)

–费尔南多·科什(Fernando Kosh)
16-11-27在21:34

如其他答案中所述,应在此处使用$ host。

– Artem Russakovskii
17年1月7日,9:25

#4 楼

如果您正在使用新的双重HTTP和HTTPS服务器定义,则可以使用以下命令:

server {
    listen   80;
    listen   [::]:80;
    listen   443 default ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;

    if ($ssl_protocol = "") {
       rewrite ^   https://$server_name$request_uri? permanent;
    }
}


这似乎对我有用,并且不会导致重定向循环。

编辑:

已替换:

rewrite ^/(.*) https://$server_name/ permanent;


使用Pratik的重写行。

评论


@DavidPashley,您的解决方案对我来说就像一个魅力。谢谢

–贾伊什·戈帕兰(Jayesh Gopalan)
2012年5月17日12:23

如果您正在使用新的双重HTTP和HTTPS服务器定义,则应将其分开。

–VBart
13年2月23日在14:42

优雅而完美的作品!

– jacktrade
2014年9月17日于17:01

这是适用于我的Laravel / Homestead Nginx配置的唯一解决方案。

–贾里德·艾特尼尔(Jared Eitnier)
2015年1月5日于17:04

重写行也应该返回301 https:// $ server_name $ request_uri;因为这是首选方法。

–贾里德·艾特尼尔(Jared Eitnier)
2015年4月20日15:17



#5 楼

还有一个变体,保留了Host:请求标头,并在nginx陷阱上遵循了“ GOOD”示例:

server {
    listen   10.0.0.134:80 default_server;

    server_name  site1;
    server_name  site2;
    server_name  10.0.0.134;

    return 301 https://$host$request_uri;
}


这里是结果。请注意,使用$server_name而不是$host将始终重定向到https://site1

# curl -Is http://site1/ | grep Location
Location: https://site1/

# curl -Is http://site2/ | grep Location
Location: https://site2/


# curl -Is http://site1/foo/bar | grep Location
Location: https://site1/foo/bar

# curl -Is http://site1/foo/bar?baz=qux | grep Location
Location: https://site1/foo/bar?baz=qux


评论


请注意,使用$ server_name代替$ host总是会重定向到https:// site1,这不是$ request_uri的目的吗?

–于尔根·保罗(JürgenPaul)
2014-4-27 9:49



$ request_uri不包含主机名或域名。换句话说,它始终以“ /”字符开头。

–彼得
2014年4月27日在11:17

迄今为止最好的答案。

–嘘
15年7月3日在14:42

我不确定为什么这个答案投票率太低。这是唯一值得使用的工具。

– zopieux
2015年12月8日在18:02

不能相信这么多人使用$ server_name这是正确的方法

–格雷格·恩尼斯(Greg Ennis)
16-6-29在2:14

#6 楼

server {
    listen x.x.x.x:80;

    server_name domain.tld;
    server_name www.domian.tld;
    server_name ipv4.domain.tld;

    rewrite     ^   https://$server_name$request_uri? permanent;
}


我认为这种方法效果更好。 x.x.x.x是您服务器的IP。如果您使用的是Plesk 12,则可以通过更改所需域的目录“ /var/www/vhosts/system/domain.tld/conf”中的“ nginx.conf”文件来实现。保存配置后,别忘了重启nginx服务。

评论


重写^ https:// $ host $ request_uri?常驻;会是一个更好的解决方案,因为您可能在虚拟主机上有多个服务器名称

–user333008
16 Jan 20 '15:39



#7 楼

我认为这是最简单的解决方案。仅将非HTTPS和非WWW流量都强制为HTTPS和www。

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;
    }
}


编辑-2018年4月:没有IF的解决方案可以在我的文章中找到:https://stackoverflow.com/a/36777526/6076984

评论


在Nginx世界中,IF条件是否被认为是邪恶的和低效的?

–PKHunter
17年2月14日在20:09

是的,一般而言。但是对于这种简单的检查,我猜不会。我确实有一个适当的配置文件,尽管其中包含更多的代码编写,但完全避免了IF。

– stamster
17-2-22在19:29

Google建议使用301,而不是303。来源:support.google.com/webmasters/answer/6073543?hl = zh-CN

– dylanh724
18年4月17日在6:49



@DylanHunt-我只剩下303进行测试,请注意第一个处理程序设置为301,只有第二个我忘了更改:)另外,没有IF的解决方案:stackoverflow.com/a/36777526/6076984

– stamster
18年4月20日在18:27