HTTP Status: Redirection

I’ve been rather uninspired and, therefore, uninterested in blogging lately, which is why I’ve neglected to continue my series on HTTP status codes. However, while trying to figure out a quick and easy way to delete tons of received direct messages from my Twitter account, I stumbled across one of my HTTP pet peeves coupled with the odd use of a 3xx status code, which inspired me to launch into a short rant followed by a discussion on the redirection series of status codes.

First, the pet peeve: I cannot stand it when someone uses a GET request to modify information. I don’t care how secure it is because you’re checking my session and login details, sending a GET request to /direct_messages/destroy/30264861 should not do anything other that retrieve the resource located there. If using GET, the user agent thinks it’s a safe action and will not know that it should warn me that the action I’m about to take could modify information. CSRF anyone?

Come on, people! GET is a safe method. Client-side developers (among others) would do well to learn this.

Consequently, there is no resource at that location, and that also peeves me. Instead, I get a weird 302 Moved Temporarily HTTP redirect that also includes a Status: 302 Found header. WTF? Come on! It’s 2008. RFC 2616 has been out for nine years now. Is there anyone using Twitter on a user agent old enough that it only works for HTTP 1.0? (I’ll wager that I’ll get responses from people about how some mobile browsers only understand HTTP 1.0.)

This leads me to the second thing I want to discuss: HTTP redirections.

Historically (in HTTP 1.0), HTTP had two status codes for redirection: 301 Moved Permanently and 302 Moved Temporarily. However, 302 became misused and abused by user agents that didn’t follow the spec, which stated: “If the 302 status code is received in response to a request using the POST method, 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.” Instead, user agents would often simply use a GET request on the value of the Location header, redirecting the user without warning.

Take a moment now to think about this. Why would the user agent send a GET request for the value of the Location header without asking the user if it’s okay? Because the user agent thinks it’s safe to do so. Remember my earlier rant?

Nevertheless, the user agents weren’t supposed to be behaving this way, but developers used 302 Moved Temporarily as the common method for redirecting after POST actions, and following the spec would make for a cumbersome user experience, especially if I had to approve every single redirect that occurs after I POST something. (This actually happens on mobile browsers quite frequently, since the mobile browser – on my phone, at least (a RAZR) – properly implements the expected behavior, thus making a crappy experience for me.)

So, to rectify the situation, HTTP 1.1 introduced 303 See Other and then later 307 Temporary Redirect (changing the meaning of 302 to Found.)

The 303 See Other status code unambiguously states that the value of the Location header “SHOULD be retrieved using a GET method” and that the “new URI is not a substitute reference for the originally requested resource.” That is, if the user agent originally used POST to send data, then the server can tell the user agent to safely redirect to another resource using GET, and the new resource should not be expected to act on the data sent in the original POST request.

Likewise, the 307 Temporary Redirect status code unambiguously states that if it 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.” So, if I POST a form to a server that sends back a 307 Temporary Redirect, then the user agent should ask me whether I want to proceed. If I say “yes,” then the user agent will proceed to POST the data to the URI identified by the Location header.

So, a couple rules of thumb: 302 Found is still the de-facto redirection method, though it may not mean what you want it to mean or behave properly on all user agents. If you want to process a POST request and then safely redirect the user agent using GET, use 303 See Other. If you want to redirect a POST request (that is, send the posted data to a different location), use 307 Temporary Redirect. If the original request was GET or HEAD, and you simply want to redirect the user agent, then either 302 Found or 307 Temporary Redirect are proper, though I prefer the 307.

To explicitly tell user agents (and particularly search engines and proxies) that something has moved permanently and that they should update their links, use 301 Moved Permanently.