Skip to main content
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Ed Bartram and Anne Porosoff
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Ed Bartram ( @edbartram ) Anne Porosoff ( @AnnePorosoff )

The Location() Function URL-Encodes The Hash If The URL Also Contains A Query-String In Lucee 5.3.1.102

By on
Tags:

Yesterday, I ran into a rather strange regression from our migration of Adobe ColdFusion to Lucee CFML. It seems that Lucee 5.3.1.102 will URL-encode the Hash (or Fragment) portion of a location() function call; but, if and only if the requested URL also contains a query-string. If the requested URL contains Path and Hash information only, the Hash is sent through as-is.

To see this in action, I used CommandBox to setup a simple test in which I use the location() function to forward the user to one-of-two URLs that contain a hash. One URL also contains a query-string; the other does not:

<cfif structKeyExists( url, "go1" )>
	
	<!--- This version has a single "?" (query-string) before the fragment. --->
	<cflocation
		url="./target.cfm?##/this/is/path?where=here"
		addtoken="false"
	/>

<cfelseif structKeyExists( url, "go2" )>

	<!--- This version only has the fragment, no query-string. --->
	<cflocation
		url="./target.cfm##/this/is/path?where=here"
		addtoken="false"
	/>

</cfif>

<!--- ------------------------------------------------------------------------------ --->
<!--- ------------------------------------------------------------------------------ --->

<p>
	<a href="./index.cfm?go1">Go With Query-String</a>
	&mdash;
	<a href="./index.cfm?go2">Go Without Query-String</a>
</p>

As you can see, both <cflocation> calls point to a URL that contains a hash. However, one of the URLs also contains a single ? query-string before the hash.

Now, if we run this in Adobe ColdFusion 2018, as a control, we get the following behavior:

./target.cfm?##/this/is/path?where=here redirects to the URL:

target.cfm?#/this/is/path?where=here

./target.cfm##/this/is/path?where=here redirects to the URL:

target.cfm#/this/is/path?where=here

As you can see, both <cflocation> calls work as you might expect.

Now, if we run this same code in Lucee 5.3.1.102, we get the following behavior:

./target.cfm?##/this/is/path?where=here redirects to the URL:

target.cfm#%2Fthis%2Fis%2Fpath%3Fwhere%3Dhere

./target.cfm##/this/is/path?where=here redirects to the URL:

target.cfm#/this/is/path?where=here

Notice that the first Lucee redirect - the one contains both the hash and the query-string - points to a URL in which the hash has been URL-encoded:

#%2Fthis%2Fis%2Fpath%3Fwhere%3Dhere

In my context, this hash is what is driving the location of an Angular.js application. As such, having a URL-encoded hash breaks the functionality of the Angular app. To put in a stop-gap solution for this issue, I placed this snippet of code at the very top of the CFML page that serves the Angular application shell:

<script type="text/javascript">
	// Attempt to fix Lucee hash-encoding bug. It seems that Lucee will incorrectly
	// encode the hash if the primary URL has a query-string. As such, if the Angular
	// route starts with an encoded-slash, we know that we have to decode the hash.
	(function() {

		var angularRoute = window.location.hash.slice( 1 );

		if ( angularRoute.indexOf( "%2F" ) === 0 ) {

			window.location.hash = decodeURIComponent( angularRoute );

		}

	})();
</script>

This code inspects the Hash to see if it looks like it has been URL-encoded. And, if so, swaps the hash with the decodeURIComponent() version. By doing this before the Angular.js app has a chance to bootstrap, it allows the Angular.js app to function normally.

It took me a while to figure out what was going on - which conditions were causing the hash / fragment to become URL-encoded. It wasn't happening consistently for all app-ingresses. So, hopefully this is helpful to anyone else that stumbles over this behavior. It feels like a bug to me, so I will see if I can open a Ticket in the Lucee project. I did try to look at the Lucee Java source code; but, I could not find anything that looked suspicious.

Want to use code from this post? Check out the license.

Reader Comments

15,688 Comments

@Tony,

Ah, very nice. I tried searching for an existing ticket and couldn't find anything. I suppose just searching in the wrong place or for the wrong thing. Looking at that ticket, though, I wonder what the spec says about the Fragment. I mean, I can clearly enter non-encoded characters in my Hash right now and the browser doesn't do anything special. So, I am not sure what the right answer is (ie, is this a bug or not).

1 Comments

Thank you Ben, for this post. I ran into this problem on a clients website. After a lot of debugging, I found your blog post, as so often, with the answer to my question.

Since the ticket ist open for quite some time, I hope it will soon be fixed.

15,688 Comments

@Sarah,

Awesome! So glad this could help. That's why I love to write about every little thing that I trip over - you never know who else might trip over the same problem 💪

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