mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Return 307 status instead of 301 when rerouting POST requests to SSL
When `config.force_ssl` is set to `true`, any POST/PUT/DELETE requests coming in to non-secure url are being redirected with a 301 status. However, when that happens, the request is converted to a GET request and ends up hitting a different action on the controller. Since we can not do non-GET redirects, we can instead redirect with a 307 status code instead to indicate to the caller that a fresh request should be tried preserving the original request method. `rack-ssl` gem which was used to achieve this before we had this middleware directly baked into Rails also used to do the same, ref: https://github.com/josh/rack-ssl/blob/master/lib/rack/ssl.rb#L54 This would be specially important for any apps switching from older version of Rails or apps which expose an API through Rails.
This commit is contained in:
parent
9ef56e5162
commit
64f9802e90
3 changed files with 43 additions and 1 deletions
|
@ -1,3 +1,23 @@
|
|||
* SSL: Changes redirect behavior for all non-GET and non-HEAD requests
|
||||
(like POST/PUT/PATCH etc) to `http://` resources to redirect to `https://`
|
||||
with a [307 status code](http://tools.ietf.org/html/rfc7231#section-6.4.7) instead of [301 status code](http://tools.ietf.org/html/rfc7231#section-6.4.2).
|
||||
|
||||
307 status code instructs the HTTP clients to preserve the original
|
||||
request method while redirecting. It has been part of HTTP RFC since
|
||||
1999 and is implemented/recognized by most (if not all) user agents.
|
||||
|
||||
# Before
|
||||
POST http://example.com/articles (i.e. ArticlesContoller#create)
|
||||
redirects to
|
||||
GET https://example.com/articles (i.e. ArticlesContoller#index)
|
||||
|
||||
# After
|
||||
POST http://example.com/articles (i.e. ArticlesContoller#create)
|
||||
redirects to
|
||||
POST https://example.com/articles (i.e. ArticlesContoller#create)
|
||||
|
||||
*Chirag Singhal*
|
||||
|
||||
* Add `:as` option to `ActionController:TestCase#process` and related methods.
|
||||
|
||||
Specifying `as: mime_type` allows the `CONTENT_TYPE` header to be specified
|
||||
|
|
|
@ -133,12 +133,20 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
def redirect_to_https(request)
|
||||
[ @redirect.fetch(:status, 301),
|
||||
[ @redirect.fetch(:status, redirection_status(request)),
|
||||
{ "Content-Type" => "text/html",
|
||||
"Location" => https_location_for(request) },
|
||||
@redirect.fetch(:body, []) ]
|
||||
end
|
||||
|
||||
def redirection_status(request)
|
||||
if request.get? || request.head?
|
||||
301 # Issue a permanent redirect via a GET request.
|
||||
else
|
||||
307 # Issue a fresh request redirect to preserve the HTTP method.
|
||||
end
|
||||
end
|
||||
|
||||
def https_location_for(request)
|
||||
host = @redirect[:host] || request.host
|
||||
port = @redirect[:port] || request.port
|
||||
|
|
|
@ -38,6 +38,16 @@ class RedirectSSLTest < SSLTest
|
|||
assert_equal redirect[:body].join, @response.body
|
||||
end
|
||||
|
||||
def assert_post_redirected(redirect: {}, from: "http://a/b?c=d",
|
||||
to: from.sub("http", "https"))
|
||||
|
||||
self.app = build_app ssl_options: { redirect: redirect }
|
||||
|
||||
post from
|
||||
assert_response redirect[:status] || 307
|
||||
assert_redirected_to to
|
||||
end
|
||||
|
||||
test "exclude can avoid redirect" do
|
||||
excluding = { exclude: -> request { request.path =~ /healthcheck/ } }
|
||||
|
||||
|
@ -57,6 +67,10 @@ class RedirectSSLTest < SSLTest
|
|||
assert_redirected
|
||||
end
|
||||
|
||||
test "http POST is redirected to https with status 307" do
|
||||
assert_post_redirected
|
||||
end
|
||||
|
||||
test "redirect with non-301 status" do
|
||||
assert_redirected redirect: { status: 307 }
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue