如何在 nginx 中强制或重定向到 SSL?

Cal*_*eed 234 ssl nginx redirect ruby-on-rails

我在子域上有一个注册页面,例如: 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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

VBa*_*art 271

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

server {
    listen      80;
    server_name signup.mysite.com;
    return 301 https://$server_name$request_uri;
}
Run Code Online (Sandbox Code Playgroud)

  • 最短的答案,在我的情况下完美地工作 (6认同)
  • 这应该是公认的答案。 (3认同)
  • 即使您设置了`proxy_set_header X-Forwarded-Proto https;`,这也不起作用,因为它会导致“重定向过多”错误 (2认同)

Pra*_*min 148

根据nginx pitfalls,省略不必要的捕获会稍微好一点,$request_uri而是使用。在这种情况下,附加一个问号以防止 nginx 将任何查询参数加倍。

server {
    listen      80;
    server_name signup.mysite.com;
    rewrite     ^   https://$server_name$request_uri? permanent;
}
Run Code Online (Sandbox Code Playgroud)

  • 或者,根据您链接的站点,*“更好”*:`return 301 http://domain.com$request_uri;` (71认同)
  • 一个评论。$server_name$ 选取第一个 server_name 变量。因此,如果您的配置中有非 FQN 名称,请注意这一点 (13认同)
  • @nh2 这是文档错误的另一种情况,因为使用 `return 301...` 会导致“重定向过多”错误,而重写方法实际有效。 (2认同)
  • 现在记录为“也不好”。@MikeBethany `return 301` 确实有效,除非(我猜)您通过监听两个端口(配置示例。触发问题:以 http://serverfault.com/a /474345/29689 的第一个答案并省略 if)。 (2认同)

DEL*_*ACC 132

如果您想将所有内容都保存在一个服务器块中,这是正确且最有效的方法:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的所有其他内容,使用“重写”或“如果 ssl_protocol”等都更慢更糟。

这是相同的,但更有效,通过仅在 http 协议上运行重写,它避免了在每个请求上检查 $scheme 变量。但说真的,这是一件小事,你不需要将它们分开。

server {
    listen   80;
    listen   [::]:80;

    server_name www.example.com;

    return 301 https://$server_name$request_uri;
}
server {
    listen   443 default_server ssl;

    server_name www.example.com;

    ssl_certificate        /path/to/my/cert;
    ssl_certificate_key  /path/to/my/key;
}
Run Code Online (Sandbox Code Playgroud)

  • 太好了,即使这个答案是正确的,一些懦夫也没有说明原因就否决了这个答案。也许是那些“如果是邪恶的”邪教徒中的另一个。如果您费心阅读有关 If 的 Nginx 文档,您就会知道 IfIsNOTEvil,只是在 location{} 上下文中使用它,我们在这里没有做。我的回答绝对是正确的做事方式! (8认同)
  • 我没有对此投反对票,但我想指出,在最新版本中,默认值已更改为“default_server”。 (2认同)

Dav*_*ley 55

如果您使用新的双 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

编辑:

替换:

rewrite ^/(.*) https://$server_name/$1 permanent;
Run Code Online (Sandbox Code Playgroud)

与 Pratik 的重写线。

  • @DavidPashley 你的解决方案对我来说就像一个魅力。谢谢 (2认同)
  • 优雅,完美! (2认同)
  • 这是唯一适用于我的 Laravel/Homestead Nginx 配置的解决方案。 (2认同)

Pet*_*ter 29

另一个变体,它保留了 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;
}
Run Code Online (Sandbox Code Playgroud)

这是结果。请注意,使用$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
Run Code Online (Sandbox Code Playgroud)

  • 我不确定为什么这个答案的票数如此之低。它是唯一值得使用的。 (3认同)
  • `$request_uri` 不包含主机名或域名。换句话说,它总是以“/”字符开头。 (2认同)
  • 迄今为止最好的答案。 (2认同)
  • 不敢相信这么多人使用 $server_name 这是正确的做法 (2认同)