Skip to main content
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Jamie Krug
Ben Nadel at cf.Objective() 2010 (Minneapolis, MN) with: Jamie Krug@jamiekrug )

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

By on

Yesterday, I ran into a rather strange regression from our migration of Adobe ColdFusion to Lucee CFML. It seems that Lucee 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. --->

<cfelseif structKeyExists( url, "go2" )>

	<!--- This version only has the fragment, no query-string. --->


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

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

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 redirects to the URL:


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

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

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


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


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:


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 );



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



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).


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.



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 💪