mdk.fr/content/blog/post-data-lost-on-301-moved...

73 lines
3.1 KiB
Markdown

Title: Post data lost on 301 Moved Permanently
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...