[mod_python] cookies and redirects

Byron Ellacott bje at apnic.net
Mon Nov 8 19:16:44 EST 2004

mike bayer wrote:
> I have never seen a 307 redirect used before, so I will admit my ignorance
> of it as well as its usablity.  But big sites like Yahoo! etc. use "302
> Found" combined with the Location: header (I just double checked) to send
> the client elsewhere, as does the "Redirect" directive in Apache.

Hmm, looks like I've gotten my codes a little mixed up again, I'm 
afraid.  (The following discussion is for HTTP/1.1 communication, see 
further below for more on this.)  The 307 return code, or "Temporary 
Redirect," indicates to the client that the request should be redirected 
to the given Location.

The code I meant to give was the 303 code, which is the HTTP "See Other" 
code.  This code instructs the client that the response to their request 
is at the given Location, and that the client should use GET to fetch 
that URI.  To directly quote the HTTP/1.1 spec:

   This method exists primarily to allow the output of a POST-activated
   script to redirect the user agent to a selected resource.

> Which is not to say the 307 is not a better way, but is it very well
> supported and predictable across all browsers ?

The 303 (and 307) return codes are supported by HTTP/1.1 browsers.  Even 
Internet Explorer appears to handle them correctly.

Since they're HTTP/1.1 codes, you should only return them to HTTP/1.1 
clients.  I don't have any Python code currently doing this, though I do 
have Perl code that is.  I'll attempt to show you what the Python code 
might look like:

   def _redirect(req, uri):
       if req.proto_num >= 1001:
           req.err_headers_out.add('Location', uri)
           raise apache.SERVER_RETURN, apache.HTTP_SEE_OTHER

> If you really want a seamless redirect with zero side effects, an internal
> redirect, i.e. an server-side change of output within the scope of one
> request, is best, if your application can support them.

That depends on what you mean by 'zero side effects.'  Chances are, when 
you do a redirect what you're really saying is that the user should be 
looking at the new URI.  If you do an internal redirect, the user will 
still be seeing the original URI in their address bar.  If they reload, 
they will be reloading that original URI.  If they go forward/back in 
history, they will be visiting that original URI.  If they bookmark, 
they will be bookmarking the original URI.

At best, this means they'll be seeing the wrong URI and possibly the 
wrong output (think bookmarked pages).  At worst, this means they'll do 
an action twice when they try to refresh what they're seeing.

A 303 redirect, on the other hand, 'seamlessly' tells the client browser 
that it should be using the new URI to show the user what's happening. 
In all recent browsers that I've tested (Opera, Safari, Gecko, and IE >= 
5) this is exactly what happens - the new URI is shown in the Location 
bar, going back/forward in history goes to the new URI, and reloading 
does another GET of the new URI.  The original, probably POSTED URI is 
no longer visible or seen, which is IMO almost always what you want when 
you're doing a dynamic redirect.

> Also, an external redirect is often used specifically to prevent reloads
> and back button usage, as in the case of an ecommerce submit form that you
> dont want submitted a second time by accident.

We-eel, it's a little more than that.  Consider a database of clients, 
identified by an ID of some sort.  You have a page to fetch a client by 
ID, where you can enter the client's ID and POST to the server to find 
the client's details page.  This is a fairly typical sort of thing to 
do, as far as I'm aware.

Let's say, just for kicks, that client #44's details can be found at 
/clients/44/ and that the search page is at /clients/.

So, your user visits /clients/ and POSTs their request to view #44.  If 
you're doing an internal redirect, their URL bar will probably still 
show '/clients/' or some other URL where the search handler exists.  If 
you're doing a 303 redirect, their URL bar will show '/clients/44'. 
They can bookmark this, reload it without worrying about POST resubmits, 
and leap around their history with unsurprising results.

It works if you use '/clients/display.html?id=44' as well, but there's a 
number of difficulties in using query fragments to track resources that 
I'm sure most of you have already encountered.  (What happens when you 
POST to '/clients/display.html?id=44'?  Shouldn't you have put the id 
into a hidden form field?  Whatever you do, don't put it into a cookie! :)

> I tend to use external redirects only for that reason, i.e. to prevent
> form reloads, and otherwise internal redirects for everything else, which
> are particularly useful since you can more easily preserve the request
> state among the original and redirected pages.

The only reason [that I can see] to do a redirect, other than after a 
form submission, is to show a resource other than the one requested.  If 
you're loosely following REST priciples, this shouldn't arise at all. :)

(As a side note, one of the web apps I've helped develop does a redirect 
when you GET its index page, to a start page that exists in the users 
preferences.  In this case, I still recommend a 303 redirect, since the 
URL location bar /should/ be updated to reflect the 'real' location.)


More information about the Mod_python mailing list