Is Ghost lower casing the URL?

  • What version of Ghost are you using? 2.5.0

I’m using dynamic routing, and i have something like this:

    data: page.about
    redirect: false
      - custom-team

The url is this: /about/team/Z2lkOi8vdGVhbS9tZW1iZXIvMTcwMTg5Nzg5NjAyNQ==/
Notice the :id has upper and lower case.
When loading the page it is transformed to: /about/team/z2lkoi8vdgvhbs9tzw1izxivmtcwmtg5nzg5njaynq==/

For whatever reason the pathname gets all lowercase. Is there a way to not make it lowercase?


To answer my own question. Yes, Ghost does this. With uncapitalise.js
But why?

Is there a way to add an option to the routes.yaml so it explicitly tells not to lowercase anything at that given URL?


Where is this ID coming from?


Why do Ghost need this functionality? To make slugs look better? According to
“URLs in general are case-sensitive (with the exception of machine names). There may be URLs, or parts of URLs, where case doesn’t matter, but identifying these may not be easy. Users should always consider that URLs are case-sensitive.”

Anyway, this problem can be easily solved by flagging uppercase letters or use a non case-sensitive ID.


From where that string comes is not relevant to solving the problem.


I might do something with 3rd party APIs and I need to pass some case sensitive strings in the URL.
I think this is a problem with Ghost. It should not redirect to an all lowercase URL.
First I thought the browser was acting up. Then I checked on youtube and all worked fine (yes they have upper and lower case strings in the URL). So the only problem provider left was Ghost.
If you check in uncapitalise.js it has exceptions for signup and api related URLs.
Why? There is no logical reason for this.
It sounds like a bug to me.


Im guessing you’re doing some sort of checking? Ghost does not allow such checking…yet. I wanted to achieve something similar but and went with a separate server to handle any POST requests.


I did same kind of routing as Robert_Calin and I registred a custom handlebar helper to read data from handlebars object “”. I my case its was overkill to register a separate server for doing some simple post requests.


@Robert_Calin Ghost supports slug or ObjectId format URLs which are all lowercase, the redirect is in there to prevent accidental 404s when linking (this has been a problem in the past) and avoid duplicate content penalties via serving the same content on multiple URLs if URLs were case-insensitive.

Regarding your use-case I’m not sure I’m following. Ghost can’t do anything with the :id parameter that you are passing in. If you’re using it to pass on to a client-side script then you also have some potential problems with duplicate content because every page will have exactly the same content and a malicious actor could generate multiple links with bogus :id values which will all render exactly the same content.

For passing data to client-side scripts you’re better off using an anchor or query param, otherwise it sounds like you’re trying to do something that should be done outside of Ghost.


There is no need to show exactly the same content when :id can be read inside a custom handlebars helper. Then you can return custom data from the async helper using Ghost API or own queries. I cant understand why this should be done outside of Ghost if the task and query is minimal. For large apps I maybe understand why.
I love the Ghost-App module and I wish and hope for furture support.


I want to put in the url a youtube video id, that contains uppercase letters. And please don’t tell me to use a query parameter. And what I do later with that piece of string, or any other string in the url is my problem to solve, but don’t but barriers in the way.
Since Ghost allows dynamic routes, then let it be dynamic.
This is an unexpected behaviour.
Try not to fix a problem that does not exist.

Also do you restrict 99% of use cases because 1% of edge case?


A youtube video id in the url? That seems… interesting.

Ghost is a publishing platform designed for the needs of web publishers. As @Kevin has mentioned the post objectid urls are generated “slugs”. This is a common term in publishing and a common pattern (lowercasing, replacing whitespace and substituting special characters) that you’ll find on nearly all publishing platforms:




So I think that Ghost fits 99% of use-cases and perhaps doesn’t the 1% edge case that putting youtube video id’s in urls falls into.

Aside from the commonality of the lowercase slug pattern there’s another reason slugs get lowercased: MySQL. By default MySQL case insensitive:

mysql> INSERT INTO Posts VALUES ('slug-that-is-lowercase') ;
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO Posts VALUES ('slug-that-is-LOWERCASE') ;
ERROR 1062 (23000): Duplicate entry 'slug-that-is-LOWERCASE' for key 'slug'

mysql> SELECT * FROM Posts WHERE slug='sLuG-tHAt-iS-loWerCAse' ;
| slug                   |
| slug-that-is-lowercase |
1 row in set (0.01 sec)

That could potentially add more headaches and edge cases to work with. Caching of URLs could also become an issue.

Have you considered passing the video id in a query parameter? Just like Youtube does:


To anyone who wants POST functionality and receive uppercase letters:

Replace mountRouter() in /server/services/routing/ParentRouter.js with this:

mountRouter(path, router, type) {

    if (arguments.length === 1) {
        router = path;
        debug( + ': mountRouter: ' +;

    else if(arguments.length === 3 && (typeof type !== "undefined")) {

        if(type == "POST") {
            registry.setRoute(, path);
  , router);


    else { 

        registry.setRoute(, path);
        debug( + ': mountRouter: ' + + ' at ' + path);
        this._router.use(path, router);

Then register router in custom app with POST-argument like this:

ghost.routeService.registerRouter("/YOUR/PATH", function labsEnabledRouter(req, res, next) {

var postData = "";

req.on('data', function(chunk){ data += chunk})

req.on('end', function(){
	// postData contains postdata

}, "POST");


@joakimkm Exactly.

@tim-ghost check this out:
If what you say is true, then others would do it too, but they don’t.

Why can’t we agree on the fact that what Ghost is doing now is an unexpected behaviour?


For the reasons that Tim just very kindly explained in some detail. You’ve been given a clear answer - it may not be the one that you want to hear, but it’s not going to change at present.


@John, so you are telling me to go fish on another platform? Basically that’s what I’m reading.
Is Ghost an open source platform? Does Ghost intend to serve a community?


You should do whatever you think is best based on your needs, Robert - that’s completely your choice - but it’s unrealistic to demand others bend to your will just because you think they should.

Use Ghost. Fork Ghost. Don’t use Ghost. Enjoy the free society of the internet where there are alternatives and opportunities abound, and you are neither trapped nor forced to use our free software if you don’t want to.


I’m not asking anybody to band to my needs.
I’m asking to make this a bug. I’ll fix it myself and push the code.

Why can’t you understand that redirecting to a different case URL is something unexpected? Also this “feature” is undocumented. I had to waste 2 hours of my life figuring out what is going on.

It is ok to have an opinionated platform, but the redirection is too much of an opinion.