Sanity Checking HTTP Method Used After Location() Call In ColdFusion
Over the weekend, I read the book Hypermedia Systems by Carson Gross. The book lays out a rather compelling argument for the use of HTML and hypermedia as the foundation for most web applications. And, as I read the book, I found myself nodding in violent agreement with most of what was being discussed. Except for something that he said with regard to URL redirection. In his chapter on "htmx patterns", he mentioned that URL redirection should be performed with a 303 See Other
status code otherwise the incoming HTTP method would be propagated to the next page:
Unfortunately, there is a problem with our updated handler: by default, in Flask the
redirect()
method responds with a302 Found
HTTP Response Code.According to the Mozilla Developer Network (MDN) web docs on the
302 Found
response, this means that the HTTP method of the request will be unchanged when the redirected HTTP request is issued.We are now issuing a
DELETE
request with htmx and then being redirected to the/contacts
path by flask. According to this logic, that would mean that the redirected HTTP request would still be aDELETE
method. This means that, as it stands, the browser will issue aDELETE
request to/contacts
.This is definitely not what we want: we would like the HTTP redirect to issue a
GET
request, slightly modifying the Post/Redirect/Get behavior we discussed earlier to be a Delete/Redirect/Get.Fortunately, there is a different response code,
303 See Other
, that does what we want: when a browser receives a303 See Other
redirect response, it will issue aGET
to the new location.
I thought to myself, there's no way this could be true. But, according to the specification, a 302
response should keep the same HTTP method when redirecting. But surely, if this were the case, I would've known about it after so many years in web development.
I wanted to run a quick sanity check in ColdFusion. So, I set up a simple script that logs the incoming HTTP status code. And, presents a form in which the user can select the HTTP status code to be used in the location()
call that is invoked post-form-processing as part of the Post/Redirect/Get control flow pattern:
<cfscript>
// Utility method to write to standard-out stream in Adobe ColdFusion.
io = ( message ) => writeDump( var = message, output = "console" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
param name="form.submitted" type="boolean" default=false;
param name="form.useStatus" type="numeric" default=302;
// Record the incoming HTTP status code for the request.
io( "[#cgi.request_method#]: #cgi.script_name#" );
if ( form.submitted ) {
// Record the requested redirection code for the location() call.
io( "Form processing with [#form.useStatus#] status code...." );
location(
url = "./test.cfm?processed=true",
addToken = false,
statusCode = val( form.useStatus )
);
}
</cfscript>
<!doctype html>
<html lang="en">
<body>
<h1>
Testing Location() Status Code
</h1>
<form method="post" action="./test.cfm">
<fieldset>
<legend>
Use HTTP Status Code in Location()
</legend>
<label>
<input type="radio" name="useStatus" value="301" />
301 - Moved Permanently
</label>
<label>
<input type="radio" name="useStatus" value="302" checked />
302 - Found (default for <code>location()</code> method)
</label>
<label>
<input type="radio" name="useStatus" value="303" />
303 - See Other
</label>
</fieldset>
<button type="submit" name="submitted" value="true">
Submit Form
</button>
</form>
</body>
</html>
As you can see, at the top of each request, I'm logging the cgi.request_method
method being used to access the current page. If I then try to submit the form using each of the three options, I see this in the standard-out stream (I've removed some of the noisy parts from the logging):
[GET]: test.cfm
[POST]: test.cfm
Form processing with [301] status code....
[GET]: test.cfm
[POST]: test.cfm
Form processing with [302] status code....
[GET]: test.cfm
[POST]: test.cfm
Form processing with [303] status code....
[GET]: test.cfm
As we can see, the browser issues GET
request in response the redirection mechanics no matter if we chose a 301
, 302
, or 303
HTTP response code after our form processing.
Of course, we didn't actually need to do logging to confirm this. We could've looked right in the browser dev-tools to see which method is being used in the redirection. I tried this on Chrome, Firefox, and Safari and they all behave exactly the same way, with a GET
being used when the Location
header has been sent via ColdFusion's location()
built-in function:

As we can see in the network activity tab, the browser (Chrome) is issuing a GET
request after receiving each of the 301
, 302
, and 303
redirection headers.
Aside: If you try to use a
307 Temporary Redirect
, my demo script will redirect indefinitely (until the browser errors-out) because, with a307
, thePOST
is propagated to the subsequent location.
My guess here is that we're dealing with an HTTP_REFERER
kind of situation ("referer" was accidentally misspelled in the early days of the web). Meaning, it's very possible that the original specification for the 302
status code intended for the method to be propagated to the next URL. However, the major browsers didn't adhere to the specification, and instead used a GET
during the redirection. And now, that ship has sailed. And, any attempt to adhere to the original specification would be a massive breaking change for the entire web.
And, given the fact that the greater web ecosystem has a strict "never break the web" mentality, I can't imagine that you'll ever run into a problem using a 302 Found
redirect after form processing. So, while I appreciate that the authors of Hypermedia Systems are trying to adhere to the HTTP specification on this point, it seems entirely unnecessary for anyone to start using 303 See Other
.
Want to use code from this post? Check out the license.
Reader Comments
From the RFC for
302
, it states:Presumably this would be
SHOULD
for any browser based client. However, if you have a server-to-server client (such as a non-browser API consumer), I suppose you could expect the302
to retain the HTTP method since redirection in an API client context is much less meaningful than in a browser context.Have you tested to identify how other clients handle 303 redirects? (ie, CFHTTP, CFX_HTTP5, CURL, WGET, etc?)
I used CURL (on Windows) with the
location
flag enabled to connect to an IIS web server:The verbose output below seemed to indicate
Switch to GET
, but then continued to perform what looks like a POST in the output.@James,
I did not test anything else but browsers. Mostly I was trying to verify that sticking with
302
as "OK" to do. I'm not too familiar withcurl
, but looking at your output, yeah, it definitely seems like aPOST
is taking place at the very end there ... but, it's very possible that I'm not reading it properly.Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →