Laravel 5 - htaccess HTTPS redirect on post routes

2019-01-28 22:28发布

问题:

I have an application built with Laravel 5 acting as an API. I have the following in my .htaccess file to redirect all routes to https:

  RewriteEngine On

    # Force SSL
    RewriteCond %{HTTPS} !=on
    RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

This works fine for the following:

https://example.com/route

but throws a MethodNotAllowedHttpException if i try to access it with http. It correctly redirects to https, but seems to be not correctly following with the POST request, since all my routes only allow POST.

Is there any way to fix this without changing my routes to allow GET as well?

回答1:

In order to force https, your RewriteRule is required to force an external redirect. In the case of your rule, you're forcing a 301 redirect (R=301). Most browsers and clients are designed to automatically follow redirects, but they (incorrectly) do so with a GET request.

So, when a client sends a POST request with data to http://example.com/route, your server responds with a 301 redirect to https://example.com/route, and then the client makes a GET request without the data to the new URL. As you can see, you can't just change your routes to accept GET requests as well, because the data sent in the original POST request will be dropped.

One option would be to add a rewrite condition to not redirect POST requests:

# Force SSL
RewriteCond %{HTTPS} !=on
RewriteCond %{REQUEST_METHOD} !=POST
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

However, this pretty much defeats the purpose of what you're attempting to accomplish. This whole concept is to force https everywhere. It would be silly to enforce it everywhere except when POSTing data to your application.

Another possible option would be to change your 301 redirect to a 307 redirect. Even though clients aren't supposed to change the request method for 301/302 redirects, most clients do due to ambiguity in the specs and existing functionality. Because of this, 307 was added with the specific idea that if this status code is used, the request method MUST NOT be changed. However, 307 is classified as a "temporary redirect", so it won't be cached and may affect your SEO. Additionally, this status was added in the HTTP/1.1 spec, so you may run into some clients that don't know how to process a 307.

Your best option is probably just to reject non-secure POST requests. Or, redirect them to an error page explaining you don't accept non-secure POST requests.

RewriteEngine On

# Forbid non-secure POSTs
RewriteCond %{HTTPS} !=on
RewriteCond %{REQUEST_METHOD} =POST
RewriteRule ^ / [F,L]

# Force SSL
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]