Inspecteur d’encodage en pourcentage

L’encodage en pourcentage n’est pas une règle unique mais plusieurs, et elles divergent sur une douzaine de caractères. Cette page fait passer le même texte par encodeURIComponent, encodeURI et la sérialisation application/x-www-form-urlencoded côte à côte, marque les caractères où elles divergent, affiche une ligne par caractère avec ses octets UTF-8 et sa classe RFC 3986, et indique combien de fois une chaîne a été encodée. Le décodage lit une chaîne selon une règle stricte ou tolérante et indique quel échappement est cassé et où, au lieu de renvoyer un résultat silencieusement faux.
Tout le traitement s’effectue localement dans votre navigateur — vos données ne sont jamais envoyées à un serveur.

Vous ne savez pas ce qu’est l’encodage en pourcentage ? L’encodage en pourcentage expliqué simplement — ce que c’est, pourquoi une espace devient %20 ou +, et ce que signifie un %25 dans une URL.
Ce que fait cet outil :
Comment lire les échappements :
encodeURIComponent
encodeURI
formulaire (application/x-www-form-urlencoded)
Ctrl+Entrée dans la saisie lance le mode actif.
Choisissez un mode et cliquez sur son bouton d’action. Encoder et inspecter affiche les trois encodages de votre texte plus une ligne par caractère ; Décoder relit les échappements et compte les couches. Le bouton Autotest relance chaque exemple documenté, chaque caractère ASCII imprimable et les chaînes de couches enregistrées dans le fichier de données de cette page, et en affiche le nombre ici.
Là où les encodeurs divergent :seuls les caractères ci-dessous sont traités différemment par les trois méthodes. Tous les autres passent par les trois sans changement, ou sont échappés de manière identique par les trois.
Chaque caractère, une ligne chacun :les octets UTF-8 dans lesquels un caractère se transforme, la classe que lui attribue la RFC 3986 et l’échappement qu’écrit chaque contexte. Les quatre colonnes de contexte sont les jeux d’encodage en pourcentage de l’URL WHATWG, c’est-à-dire ceux qu’utilise un navigateur pour construire un chemin, une requête ou un fragment ; une ligne ombrée est un caractère que les trois encodeurs traitent différemment.
L’ensemble exact que cette page échappe pour chaque partie d’une URL :imprimé à partir de l’encodeur lui-même, de sorte que ce tableau et le code qui remplit les colonnes ci-dessus ne peuvent pas diverger. Chaque jeu échappe aussi les caractères de contrôle C0 (U+0000 à U+001F) et chaque point de code au-delà de U+007E, ce qui explique pourquoi le texte non ASCII est toujours échappé dans tous les contextes.
Ces jeux sont dérivés des jeux d’encodage en pourcentage de WHATWG et en diffèrent à deux endroits délibérés, qui rendent l’assemblage à la main sûr : un dièse est échappé dans une requête, car un dièse brut commencerait le fragment ; et le jeu des composants est fixé exactement aux caractères qu’échappe encodeURIComponent, apostrophe comprise, tandis que le jeu de formulaire est fixé à ce qu’échappe URLSearchParams. Les deux colonnes du tableau ci-dessus reflètent donc le comportement propre de la plateforme et non une réimplémentation. Cette liste explique aussi un résultat que beaucoup trouvent surprenant : un signe pourcent est échappé par encodeURIComponent et par encodeURI, mais il n’appartient à aucun des jeux de contexte WHATWG, si bien que les colonnes chemin, requête et fragment du tableau par caractère le laissent intact. Un signe pourcent brut dans une URL assemblée à la main reste un risque, car la norme suppose que l’entrée a été validée au préalable : %41 dans une requête est un simple A pour de nombreux frameworks serveur, et la lecture stricte de cette page traite un % isolé comme un échappement cassé. L’échapper soi-même en %25 est l’habitude la plus sûre, et le tableau par caractère montre les deux écritures côte à côte.
Nombre de fois où cette chaîne a été encodée :chaque tour de décodage correspond à une couche. Une valeur encodée deux fois commence par%25là où un encodage unique laisserait%20, et une valeur encodée trois fois commence par%2525.
Les caractères qu’une URL peut porter tels quels :La RFC 3986 répartit la plage ASCII en trois groupes, et les règles d’échappement n’ont de sens que par rapport à cette répartition.
Les caractères réservés ne sont pas interdits, ils sont pris : ils ont un sens pour l’analyseur d’URL. La règle qui en découle est simple — si un caractère est réservé et que vous voulez le traiter comme une donnée, échappez-le ; s’il est non réservé, vous n’en avez jamais besoin.
Les trois méthodes, et ce que chacune laisse intact :
Les listes sont les jeux exacts que chaque méthode laisse non échappés dans la plage ASCII ; l’Autotest vérifie chaque caractère ASCII imprimable par rapport à elles, de sorte que le tableau ne peut pas s’éloigner des encodeurs qu’il décrit.
Taille maximale de saisie acceptée par cette page, et pourquoi :l’encodage en pourcentage est une seule passe sur le texte ; contrairement aux convertisseurs de bases numériques de ce site, le coût croît donc linéairement et non au carré : il n’existe aucune longueur à partir de laquelle le calcul lui-même devient le problème. Les plafonds ci-dessous existent parce que le tableau par caractère est dessiné dans le document, et qu’un tableau d’un million de lignes est précisément ce qui bloque un navigateur.
Qu’est-ce que l’encodage en pourcentage ?

L’encodage en pourcentage — aussi appelé encodage d’URL, et dans les anciens documents échappement — est la règle qui permet à une URL de porter des caractères qu’elle n’a pas le droit de contenir. Un échappement de pourcentage est formé de trois caractères ASCII : un signe pourcent et deux chiffres hexadécimaux, qui représentent un octet. %20 est l’octet 0x20, qui est une espace. %2F est l’octet 0x2F, qui est une barre oblique. Rien d’autre n’intervient : l’échappement désigne un octet, et non un caractère.

Comme il désigne des octets, le texte doit d’abord être transformé en octets, et l’encodage des octets est l’UTF-8. C’est d’où viennent les longs échappements : un caractère chinois fait trois octets UTF-8, donc trois échappements, et un emoji fait quatre octets, soit douze caractères ASCII.

La version en 30 secondes : une espace vaut %20 comme valeur d’URL mais + dans des données de formulaire ; # vaut %23 ; un signe pourcent littéral vaut %25. Les trois encodeurs de cette page s’accordent sur les lettres, les chiffres et -_., et divergent sur une douzaine de signes de ponctuation, le tilde parmi eux — c’est précisément pourquoi encodeURIComponent et encodeURI produisent des chaînes différentes pour le même texte, et pourquoi le mauvais des deux peut scinder une valeur en deux paramètres de requête.
Pourquoi une URL en a besoin

Un URI est défini comme une suite de caractères ASCII (RFC 3986, section 2.1), et dans cette plage ASCII certains caractères sont réservés : ce sont les signes de ponctuation qui donnent sa structure à l’URL. Deux-points et deux barres obliques séparent le schéma, l’arobase sépare les informations utilisateur de l’hôte, le point d’interrogation commence la requête, le dièse commence le fragment, l’esperluette sépare les paramètres de requête, et le signe pourcent introduit un échappement.

Cela crée un problème et une faille. Le problème : une valeur qui contient elle-même l’un de ces caractères — une zone de recherche contenant « a&b », un nom de fichier contenant une barre oblique, un mot de passe contenant deux-points — modifierait autrement la structure de l’URL dans laquelle elle voyage. La faille : le signe pourcent lui-même, de sorte que l’encodeur peut simplement étendre son propre alphabet. % s’écrit %25, et les deux chiffres hexadécimaux qui suivent sont des caractères non réservés ordinaires.

Exemples commentés

Voici les sorties exactes des trois encodeurs de cette page, pour des entrées choisies parce qu’elles se comportent différemment les unes des autres. La page les recalcule sous vos yeux, et son Autotest revérifie chaque ligne par rapport aux implémentations propres de la plateforme.

Les trois méthodes, et quand chacune est la bonne

La version courte qui évite la plupart des bogues : tout ce qui entre dans une seule valeur de paramètre passe par encodeURIComponent (ou par un sérialiseur de formulaire, qui fait le même travail avec un jeu sûr différent), et une URL complète déjà assemblée passe par encodeURI si elle doit être échappée. Prendre le mauvais des deux est l’erreur d’encodage en pourcentage la plus fréquente dans le code web.

Les caractères qu’une URL peut porter, dans leur intégralité
Deux signes plus, deux sens

Une espace est le seul caractère dont l’encodage dépend de l’endroit où le texte va, et le signe plus en est la raison.

Le piège se trouve au milieu de ce tableau. Dans les données de formulaire, un signe plus signifie une espace : une valeur qui contient réellement un signe plus — un numéro de téléphone écrit +44 20 7946 0958, une expression arithmétique, une adresse e-mail avec une étiquette en plus — doit voyager sous la forme %2B. S’il voyage comme signe plus littéral, le destinataire le relit comme une espace, et +44 arrive comme « 44».

Encodé deux fois, et comment le reconnaître

Un double encodage se produit lorsqu’une chaîne est déjà échappée puis échappée à nouveau. Le signe pourcent du premier échappement est un caractère comme un autre : il devient %25, et la couche sous-jacente subsiste comme texte visible.

Le reconnaître revient à compter : décodez la chaîne une fois et regardez le résultat. S’il contient encore des échappements qui semblent volontaires — un %XX valide ou un signe plus de formulaire là où une espace est attendue — il y a une autre couche. S’il contient %25 suivi de deux chiffres hexadécimaux, la couche suivante est certainement un échappement.

Deux remarques pratiques. D’abord, décoder deux fois n’est pas anodin : c’est une manière bien connue de faire voir à un filtre de sécurité et au code qui se trouve derrière des chaînes différentes ; un filtre exécuté avant un second décodage filtre donc le mauvais texte. Ensuite, le nombre de couches indiqué ici est une propriété de la chaîne, et non une garantie d’intention : une valeur légitime peut n’avoir qu’une couche, et une valeur cassée peut en avoir deux.

Les erreurs que cette page est conçue pour détecter
  • Une valeur encodée avec encodeURI. Les séparateurs survivent, donc une valeur contenant & ou = se scinde en plusieurs paramètres de l’autre côté. Comparez les deux colonnes en haut de cette page avec l’entrée a&b=c : la colonne composant échappe les deux caractères, la colonne encodeURI les conserve.
  • Un signe plus littéral envoyé comme données de formulaire. Il arrive sous forme d’espace. Utilisez %2B.
  • Une valeur échappée deux fois « pour être sûr ». Elle n’est pas plus sûre. Le destinataire décode une fois et obtient une chaîne qui contient encore des échappements de pourcentage : c’est la même défaillance qu’un mauvais jeu de caractères, une couche plus bas.
  • Décoder ce qui n’a jamais été encodé. Un signe pourcent littéral dans un texte — 100% sure — n’est pas un échappement. Dans une lecture stricte, c’est un échappement cassé, à la position que cette page indique ; dans une lecture tolérante, il survit, et c’est pourquoi tolérante est le réglage par défaut ici et non l’inverse.
  • Croire que l’échappement doit être en majuscules. Le décodage traite %2f et %2F comme le même octet, et les deux sont valides. La majuscule n’est que l’écriture canonique que recommande la RFC 3986, et celle que produisent les autres outils.
  • Croire que l’encodage en pourcentage rend un texte sûr. Il rend le texte non ambigu dans une URL. Il n’empêche pas l’injection, ce n’est pas du chiffrement et ce n’est pas un désinfectant : l’échappement doit être la dernière étape avant l’assemblage, pour le contexte dans lequel le texte va être placé.
Réponses rapides

« Un signe plus est-il une espace dans une URL ? »

Uniquement dans les données de formulaire, et seulement dans la partie requête d’une soumission de formulaire. Dans un chemin, dans un fragment et dans la requête d’un lien ordinaire, un signe plus est un signe plus littéral. C’est pourquoi cette page propose un interrupteur pour cela au lieu de deviner.

« Pourquoi mon lien affiche-t-il %25 ? »

Parce qu’un signe pourcent a été encodé. Soit un signe pourcent littéral est passé par un encodeur — 100% sure devient 100%25%20sure — soit une chaîne contenant déjà un échappement a été encodée une seconde fois. Décodez la chaîne une fois et regardez ce qui revient : si le résultat contient encore %XX, il y a une autre couche.

« Importe-t-il qu’un slash dans une valeur de requête soit %2F ou /  ? »

Pour l’analyseur d’URL, non : les deux arrivent au serveur sous forme de barre oblique, et le jeu de requête WHATWG laisse une barre oblique intacte. Pour l’interopérabilité, parfois oui : beaucoup de frameworks serveur effectuent leur propre décodage sur une chaîne de requête brute, et quelques-uns ne s’accordent pas sur des caractères qui n’ont jamais été échappés. Lorsqu’une valeur est assemblée à la main plutôt que par un sérialiseur, échapper explicitement les caractères réservés est l’habitude la plus sûre.

« Pourquoi la colonne formulaire encode-t-elle un tilde que les deux autres laissent intact ? »

Parce que le jeu sûr de l’encodeur de formulaire est plus restreint : seulement les lettres, les chiffres et -_.*. L’encodeur de formulaire WHATWG échappe ~ ! ' ( ) alors que la RFC 3986 classe ~ comme non réservé. Les deux écritures se décodent en la même valeur ; la différence n’importe que lorsqu’on compare des chaînes octet par octet.

« Les mêmes caractères peuvent-ils apparaître non échappés dans la barre d’adresse d’un navigateur ? »

Oui, et c’est la barre d’adresse, pas l’URL. Les navigateurs affichent une forme lisible d’une URL et l’encodent à l’envoi, si bien que 中文 dans un lien devient %E4%B8%AD%E6%96%87 sur le réseau. Si vous copiez l’URL depuis la barre d’adresse, vous pouvez obtenir l’une ou l’autre forme selon le navigateur.

« Chaque échappement ici paraît valide, mais le caractère obtenu n’est pas celui attendu. Pourquoi ? »

Deux échecs différents se cachent derrière « elle a été mal décodée », et la page les signale différemment. Le premier est un échappement cassé : un signe pourcent non suivi de deux chiffres hexadécimaux, comme un 100% en fin de texte ou un %zz. C’est un problème de syntaxe : la lecture stricte le refuse et indique où il se trouve, tandis que la lecture tolérante le conserve comme texte littéral et le liste. Le second est un échappement dont l’octet est parfaitement légal mais dont les octets ne forment pas un caractère UTF-8 valide, comme %FF seul ou un caractère de trois octets tronqué en %E4%B8. Ce n’est pas un problème de syntaxe : les deux lectures renvoient donc le texte, remplacent les octets qui ne peuvent pas être décodés par le caractère de remplacement U+FFFD (« � ») et affichent un avertissement qui le signale. Cet avertissement est la partie utile : un texte qui arrive sous forme de caractères de remplacement signifie généralement que l’expéditeur a utilisé un autre jeu de caractères, ou que la chaîne a été échappée une fois de trop. L’encodage en pourcentage désigne des octets, et cette page, comme la norme des URL, lit toujours ces octets comme de l’UTF-8.

« Que ne fait pas cette page ? »

Elle ne découpe pas une URL en schéma, hôte, chemin et requête — c’est le rôle de l’Analyseur d’URL. Elle ne vérifie pas si un lien est sûr à ouvrir, elle ne contacte rien et elle ne devine pas le jeu de caractères des octets derrière un échappement : elle les lit toujours comme de l’UTF-8, ce qu’exige la norme des URL.

« Quel repli le décodage requiert-il, et comment savoir qu’il est correct ? »

Appuyez sur Autotest. Il compare les encodeurs de la page avec encodeURIComponent, encodeURI et URLSearchParams de la plateforme pour chaque caractère ASCII imprimable, relance chaque exemple des tableaux ci-dessus, revérifie les chaînes de couches enregistrées, puis indique ici le nombre de vérifications.

Normes et périmètre

L’encodage en pourcentage est spécifié dans la RFC 3986, section 2.1, avec la définition des caractères réservés et non réservés aux sections 2.2 et 2.3, et la règle de casse à la section 6.2.2.1. Les jeux qu’un navigateur utilise réellement pour construire une URL figurent dans la norme URL WHATWG, qui les répartit selon le contexte : un jeu pour le fragment, un pour la requête, un pour la requête spéciale, un pour le chemin, un pour les informations utilisateur, et le jeu plus restreint utilisé pour un seul composant. La soumission de formulaires, y compris le signe plus, est décrite dans la norme HTML sous application/x-www-form-urlencoded. Les deux fonctions encodeURI et encodeURIComponent font partie d’ECMAScript lui-même.

Cette page remplit les colonnes chemin, requête et fragment à partir de ses propres jeux par contexte, et affiche la liste exacte pour chaque contexte à côté du résultat. Ces jeux sont dérivés de ceux de WHATWG et en diffèrent à deux endroits délibérés : un dièse est échappé dans une requête, car un dièse brut commencerait le fragment, et le jeu des composants est fixé exactement aux caractères qu’échappe encodeURIComponent. Lorsqu’une fonction de la plateforme définit la réponse — encodeURIComponent, encodeURI et la sérialisation de formulaire — cette fonction est appelée directement, de sorte que ces colonnes reflètent le comportement du navigateur lui-même et non une réimplémentation.

Le texte de la page a été rédigé pour ce site. Chaque valeur des tableaux ci-dessus est produite par le code de la page au chargement et a été vérifiée par rapport aux implémentations de la plateforme dans l’outillage de test de ce site ; l’Autotest refait cette comparaison dans votre navigateur.