为什么我的 Rails 应用程序中使用force_ssl 会出现无限重定向循环?
我想让我的 API 控制器使用 SSL,所以我在 nginx.conf 中添加了另一个监听指令
upstream unicorn {
server unix:/tmp/unicorn.foo.sock fail_timeout=0;
}
server {
listen 80 default deferred;
listen 443 ssl default;
ssl_certificate /etc/ssl/certs/foo.crt;
ssl_certificate_key /etc/ssl/private/foo.key;
server_name foo;
root /var/apps/foo/current/public;
try_files $uri/system/maintenance.html $uri/index.html $uri @unicorn;
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 502 503 /maintenance.html;
error_page 500 504 /500.html;
keepalive_timeout 5;
}
,它通过了 nginx conftest,没有任何问题。我还在我的 ApiController 中添加了一个 force_ssl
指令,
class ApiController < ApplicationController
force_ssl if Rails.env.production?
def auth
user = User.authenticate(params[:username], params[:password])
respond_to do |format|
format.json do
if user
user.generate_api_key! unless user.api_key.present?
render json: { key: user.api_key }
else
render json: { error: 401 }, status: 401
end
end
end
end
def check
user = User.find_by_api_key(params[:api_key])
respond_to do |format|
format.json do
if user
render json: { status: 'ok' }
else
render json: { status: 'failure' }, status: 401
end
end
end
end
end
当我没有使用 SSL 时,它工作得很好,但现在当我尝试 curl --LI http://foo/api/auth 时.json
,我被正确重定向到 https
,但随后我继续被重定向到 http://foo/api/auth
以无限重定向结束环形。
我的路线只是
get "api/auth"
get "api/check"
我在 Ruby 1.9.2 上使用 Rails 3.2.1 和 nginx 0.7.65
I want to have my API controller use SSL, so I added another listen directive to my nginx.conf
upstream unicorn {
server unix:/tmp/unicorn.foo.sock fail_timeout=0;
}
server {
listen 80 default deferred;
listen 443 ssl default;
ssl_certificate /etc/ssl/certs/foo.crt;
ssl_certificate_key /etc/ssl/private/foo.key;
server_name foo;
root /var/apps/foo/current/public;
try_files $uri/system/maintenance.html $uri/index.html $uri @unicorn;
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 502 503 /maintenance.html;
error_page 500 504 /500.html;
keepalive_timeout 5;
}
which passes the nginx conftest without any problems. I also added a force_ssl
directive to my ApiController
class ApiController < ApplicationController
force_ssl if Rails.env.production?
def auth
user = User.authenticate(params[:username], params[:password])
respond_to do |format|
format.json do
if user
user.generate_api_key! unless user.api_key.present?
render json: { key: user.api_key }
else
render json: { error: 401 }, status: 401
end
end
end
end
def check
user = User.find_by_api_key(params[:api_key])
respond_to do |format|
format.json do
if user
render json: { status: 'ok' }
else
render json: { status: 'failure' }, status: 401
end
end
end
end
end
which worked just fine when I wasn't using SSL, but now when I try to curl --LI http://foo/api/auth.json
, I get properly redirected to https
, but then I keep on getting redirected to http://foo/api/auth
ending in an infinite redirect loop.
My routes simply have
get "api/auth"
get "api/check"
I'm using Rails 3.2.1 on Ruby 1.9.2 with nginx 0.7.65
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您不会转发有关此请求是否是 HTTPS 终止请求的任何信息。通常,在服务器中,“ssl on;”指令将设置这些标头,但您使用的是组合块。
Rack(和force_ssl)通过以下方式确定 SSL:
请参阅完整故事的force_ssl 来源。
由于您使用的是组合块,因此您需要使用第三种形式。尝试:
在您的服务器或位置块中根据 nginx 文档。
当您传入端口 80 请求时,这会将标头设置为“http”;当您传入 443 请求时,会将标头设置为“https”。
You're not forwarding any information about whether this request was an HTTPS-terminated request or not. Normally, in a server, the "ssl on;" directive will set these headers, but you're using a combined block.
Rack (and force_ssl) determines SSL by:
See the force_ssl source for the full story.
Since you're using a combined block, you want to use the third form. Try:
in your server or location block per the nginx documentation.
This will set the header to "http" when you come in on a port 80 request, and set it to "https" when you come in on a 443 request.
尝试在您的 nginx
location @unicorn
块中设置此指令:proxy_set_header X-Forwarded-Proto https;
我遇到了同样的问题并调查了 Rack 中间件处理程序(不是
force_ssl
但类似)我可以看到它期望设置该标头来确定请求是否已被 nginx 处理为 SSL。Try setting this directive in your nginx
location @unicorn
block:proxy_set_header X-Forwarded-Proto https;
I had this same issue and investigating the Rack middleware handler (not
force_ssl
but similar) I could see that it was expecting that header to be set to determine if the request was already processed as being SSL by nginx.