Skip to main content
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Bruno Lopes and Simon Free
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Bruno Lopes Simon Free

Sanity Checking HTTP Method Used After Location() Call In ColdFusion

By
Published in Comments (3)

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 a 302 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 a DELETE method. This means that, as it stands, the browser will issue a DELETE 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 a 303 See Other redirect response, it will issue a GET 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:

Chrome developer tools with netrwork activity tab showing that a GET method is used after each of the 301, 302, and 303 redirections.

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 a 307, the POST 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

15,983 Comments

From the RFC for 302 , it states:

Note: For historical reasons, a user agent MAY change the request method from POST to GET for the subsequent request. If this behavior is undesired, the 307 (Temporary Redirect) status code can be used instead.

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 the 302 to retain the HTTP method since redirection in an API client context is much less meaningful than in a browser context.

91 Comments

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:

curl -vv -L -X POST "https://www.mywebsite.com/redirect-test.cfm" -d "useStatus=303&submitted=true"

The verbose output below seemed to indicate Switch to GET, but then continued to perform what looks like a POST in the output.

...
< HTTP/2 303
< cache-control: no-cache
< pragma: no-cache
< content-type: text/html;charset=UTF-8
* Please rewind output before next send
< location: /redirect-test.cfm?processed=true
< x-content-type-options: nosniff
< strict-transport-security: max-age=31536000
< date: Wed, 19 Feb 2025 17:17:10 GMT
< content-length: 0
* Keep sending data to get tossed away
<
* Connection #0 to host www.mywebsite.com left intact
* Issue another request to this URL: 'https://www.mywebsite.com/redirect-test.cfm?processed=true'
* Switch to GET
* Found bundle for host: 0x25eff090ae0 [can multiplex]
* Re-using existing connection with host www.mywebsite.com
* [HTTP/2] [3] OPENED stream for https://www.mywebsite.com/redirect-test.cfm?processed=true
* [HTTP/2] [3] [:method: POST]
* [HTTP/2] [3] [:scheme: https]
* [HTTP/2] [3] [:authority: www.mywebsite.com]
* [HTTP/2] [3] [:path: /redirect-test.cfm?processed=true]
* [HTTP/2] [3] [accept: */*]
> POST /redirect-test.cfm?processed=true HTTP/2
> Host: www.mywebsite.com
> Accept: */*
15,983 Comments

@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 with curl, but looking at your output, yeah, it definitely seems like a POST 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

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel