The baffling thing about trailing slashes
Published: 2020-01-23 16:41:50



If someone asked the old-me, “What's the big deal about trailing slashes?”, my answer would likely be – “ah, they make URLs look cool. That's it!”.

However, I recently met with an accident of running into an issue which changed my view for trailing slashes forever.

## TL;DR

> Avoid trailing slashes at all costs, unless you have a thing for exhausting yourself on small issues.



There are times when you decide, “Okay, I can finish this in an hour, and then move on to other things”. Everything happens like you planned, except for the time barrier – you sometimes tend to take a little longer than that.

I wasted 6 hours on what could've been solved by removing a single character from a small group of files.

Now, the question is – what the heck was it?

I had made an API in Falcon (a Python web framework), of course with routes registered with trailing slashes. There were 8 endpoints. So far, I managed to debug all errors I had encountered, thanks to the awesome documentation.

This was the case until one fine day, my Insomnia client started giving me “405 Method Not Allowed” responses on endpoints which had very well been tested before. I struggled with debugging this behavior, completely discarding the idea that a trailing slash could be the source of all problems.

What I was doing was writing logic for a PUT request to a resource, which showed me the 405 thing on testing.

After reading related forums online, I started to think that I had found the culprit. People there were still unsure about which practice was better (i.e. to-trailing-slash or to not-trailing-slash). They were good at explaining things, but shared their personal preferences, expressing uncertainity about various ideology behind the REST architecture.

Any way, I removed all the trailing slashes, tested each endpoint, and yay! It worked.

The underlying problem was my structuring of routes. I had multiple routes under “/posts/”, which were -
    – “/posts/{username}” to show all posts by {username}
    – “/posts/edit?a={author}&p={post}“, to edit the specified post
    – and others...

## What went wrong

I was doing it all wrong. It was only at the end that I was able to figure it out:

In the app, I was calling the API from “/posts/edit/?a={...}&p={...}“, which was wrong in the app but worked when done from the Insomnia client.

I found that it didn't matter whether I added a trailing slash when registering the route in code. In essence, app.add_route('/posts/edit/') worked the same as app.add_route('/posts/edit'). However, calling assuming the latter works fine in most cases.

The expected behaviour was observed when “edit/?...” was changed to “edit?...”.

# Summary

PUT /posts/edit/?a=1&b=2 is completely different from PUT /posts/edit?a=1&b=2

In Flask, I found that if you registered a route as @app.route('/login'), and typed in the browser's address bar “/login/”, it would throw you a 404.

But, if you do @app.route('/login/'), it works whether or not the trailing slash is injected or not.

I hope spending 3 minutes reading this might save someone 3 hours.

If you are familiar with some of this trouble, please let me know at [my Twitter profile](https://twitter.com/auctifer).

Happy hacking
[@roshnet](https://github.com/roshnet)