Pacharapol Withayasakpunt Pacharapol Withayasakpunt
Wed 15 July 2020

encodeURIComponent is both not safe enough, and overdone

I have tested with encodeURIComponent, decodeURIComponent and URL constructor, and the results are

  "pathname": {
    "destroyed": " #.?",
    "encoded": "\"<>`{}",
    "error": {
      "Invalid URL: //": "/",
      "Invalid URL: /\\": "\\"
  "key": {
    "destroyed": "#&+="
  "value": {
    "destroyed": " #&+"
  "hash": {
    "destroyed": " ",
    "encoded": "\"<>`"
  // /[A-Za-z0-9]/ are excluded.
  "notEncoded": {
    "encodeURI": "!#$&'()*+,-./:;[email protected]_~",
    "encodeURIComponent": "!'()*-._~",
    "escape": "*+-./@_"
  // encoded with `%${x.charCodeAt(0).toString(16).toUpperCase()}`
  "notDecoded": {
    "decodeURI": "#$&+,/:;[email protected]"

The test is here.


  • Reserved characters ;,/?:@&=+$ are not equal. Some are allowed in some scenarios, some are not. And it seems that encodeURI is never safe to encode a URI segment.
  • Path params, e.g. /:segment/* on the server
    • ., .. always have wrong meanings, whether percent-encoded or not. And encodeURIComponent('.') is indeed .. /^\.{3,}$/ are ok, though.
      • It seems that escaping by prefixing with ~ is enough.
    • /, even when encoded, may throw error on some server. Not sure about \, but it seems to throw error in my test.
  • Luckily, these are always encoded. I have seen a recent post about the errors.
Encoding mess with Javascript

Encoding mess with Javascript

Transfer data by URL by encoding them is harder than it looks and you need to be careful about evil characters.
  • Not sure if non-ASCII (/[^\x00-\x7F]/) needs to be encoded. You can try it in my demo, and see if it breaks.

So, I created a library for this,



encodeURI that is safe, and doesn't do too much in a specific scenario - patarapolw/encodeuri-plus