Post data lost on 301 Moved Permanently
Posted on Tue 04 April 2017 in blog
What the hell with 301 Moved Permanently HTTP header!? I'll take an
exemple to explain my ugly problem, take, a (very ugly cause it's PHP)
index.php
, it could be retrieved by using:
- http://example.com/directory/index.php
Got the page, 200 OK
or - http://example.com/directory/
Got the page, 200 OK
or - http://example.com/directory
Got a 301 Moved Permanently Location:...
- http://example.com/directory/
Just like expected ... but...
As you can see, on the captured HTTP headers below, when you POST data on a 301 target, you'll be redirected, but unfortunately you'll lost your POST data, even worse, your request can be reforged as a GET request! Let me show you an example: Request:
POST /directory HTTP/1.1
Host: example.com
[user agent, referer, cache control, origin, content type, accept, ...]
Content-Length: 7 foo=bar
Response:
HTTP/1.0 301 Moved Permanently
Location: http://example.com/directory/
...
Reforged request:
GET /directory/ HTTP/1.1
Host: example.com
Why my data isn't kept in the reforged one!? So let's read The RFC 2616
(about HTTP...)
10.3.2 301 Moved Permanently [...] The new permanent URI SHOULD be given by the Location field in the response. Unless the request method was HEAD, the entity of the response SHOULD contain a short hypertext note with a hyperlink to the new URI(s). If the 301 status code is received in response to a request other than GET or HEAD, the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user, since this might change the conditions under which the request was issued. Note: When automatically redirecting a POST request after receiving a 301 status code, some existing HTTP/1.0 user agents will erroneously change it into a GET request.
Ahh, ok, so, for request other than GET
or HEAD
, the user agent
MUST NOT
automatically redirect to the request? Ugh, it's not the
case in IE 6, 7, 8, Firefox, nor Chrome. And, about the security,
while the server recieves the data, it can do everything, for example
send it to another server directly. Then I'm wondering, why asking to
the user if he allows his data to be transfered? Then, the RFC says
an interesting thing...
Note: When automatically redirecting a POST request after receiving a 301 status code, some existing HTTP/1.0 user agents will erroneously change it into a GET request.
But, not only HTTP/1.0 user agents, but HTTP/1.1 user agents like
browsers and frameworks (as, I encountred the problem firstly in the
.NET Framework 3.5, in HttpWebRequest
) )o:
Reforging as a GET is clearly not a good idea, but on the other hand,
as POST
requests are not idempotent, we're still happy they're not
executed twice: Imagine the POST is "Pay for that shiny new laptop",
the payment is executed, but later in the request execution, a 301 to
another server is generated, if your client re-POST the same request
on the other server, your payment may be executed
twice... outch. Respecting idempotency looks clearly more important
than generating dumb GET
, harmless, requests.
Hope it helps...