I'm using JWTs as part of an URL in order to direct a user to a specific site. I could have just used a UUID, but it's nice to have an expiry date in the link, as well as knowing whom the link is created for.

This has been working great, but when I email such an URL to someone with Outlook (maybe a specific version of Outlook), the url gets converted to all lower case. This seems like a stupid thing to do, but I guess it's ok since URLs are supposed to be case insensitive.

Would it make sense to generate JWTs with only lowercase characters?

Here is an example.

The original url, sent as a link in an email:


When some versions of Outlook and/or Exchange gets hold of this link it gets converted to this:


Unfortunately the lowercase conversion in Outlook seems to be a thing. Others have the same problem. Here is one example.

This sucks. Of course it is MS who does such a stupid thing. JWTs are supposed to be URL safe.

I guess I'll have to abandon JWTs for my links and put my links in a database instead.

  • 2
    I doubt that Outlook changes the links in the mail. There might be some security software involved which does link rewriting. This software would be terribly broken since except for the domain part links are actually case sensitive, i.e. it would break other links too. Better to figure out whats going on there. Commented Jul 9, 2021 at 14:36
  • Regardless of who is converting my links, the problem is real. To ensure that my links will work, even if a "broken" security software or even Exchange is breaking my links, I'm now encoding them myself. Every capital letter is converted to small caps and prefixed with a tilde. I prefix the token with double-tildes so my server knows that this token need to be decoded before using it as a JWT. This works great.
    – Michael
    Commented Aug 27, 2021 at 11:36

2 Answers 2


No, JWTs can never be either all lowercase or all uppercase. That's because they're a base64url encoding of a byte sequence that starts with {". All possible byte sequences starting with that, when encoded, will start with ey and then an uppercase letter from I to L. Case is relevant in the base64url encoding; aaaa encodes the octets 0x69 0xa6 0x9a, but AAAA encodes three zero octets.

It is also not the case that URLs are supposed to be case insensitive. RFC 3986, § states this:

[T]he scheme and host are case-insensitive and therefore should be normalized to lowercase.…The other generic syntax components are assumed to be case-sensitive unless specifically defined otherwise by the scheme….

Anyone serving static assets from a Unix system knows this very well, since Unix systems are generally case sensitive.

The best solution here is to find out what's breaking things here by rewriting the URL and get them to fix it. They will be breaking many, many URLs with this approach, and any reasonable implementer would have known this and not implemented case folding.

Alternately, you could re-encode the components as base32 or base32hex and then fix the case on the other end. Those encodings use only uppercase, so case folding to the proper thing would work.


I solved my problem by encoding my JWT so it only uses lowercase. I have seen other questions with the same problem so I thought I'd leave my solution here if anyone has use for it.

Mind you, that this problem is real. Even if it would be solvable by upgrading Exchange servers, change settings or remove some security software, this is not in our hands. Mail-servers and or mail-client which destroys JWTs in URLs are out there and we need to handle it.

The encoding is simple, just replace every uppercase character with it lower case version and prefix the char with tilde. The prefix character needs to be URL safe as well as not part of the JWT char set. Tilde fits the bill.

Javascript code for encoding a JWT so it uses "tilde+lowercase" for each uppercase char. I use this encoding when an URL with a JWT token is to be sent by email.

  token =
    '~~' +
    token.replace(/([A-Z])/g, function (v) {
      return '~' + v.toLowerCase();

When a token is received in a query string I'm checking if it has been "tilde-encoded" and decode it.

function decodeFromLowercase(token) {
  if (token.substr(0, 2) === '~~') {
    return token
      .replace(/(~.)/g, function (v) {
        return v[1].toUpperCase();
  } else {
    return token;

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .