8 min de lectureIngénierie

L'encodage d'URL expliqué : quels caractères échapper

L'encodage d'URL remplace un caractère par un signe pourcentage et deux chiffres hexadécimaux afin qu'il ne puisse pas être lu comme de la syntaxe URL. Quels caractères en ont besoin, et où cela casse.

Marius Voß
DevRel · edge infra
L'encodage d'URL illustré par une chaîne de requête où un espace et une esperluette deviennent des séquences en pourcentage à l'intérieur d'une valeur de paramètre

L'encodage d'URL remplace un caractère par un signe pourcentage et deux chiffres hexadécimaux : un espace devient %20, une esperluette devient %26, un point d'interrogation devient %3F. Le but est d'empêcher qu'un caractère soit lu comme de la syntaxe URL quand vous le vouliez comme donnée. Rien de plus.

La raison pour laquelle cela semble plus compliqué, c'est que presque toute question sur le sujet est en réalité une question de portée. Quels caractères, dans quelle partie de l'URL, échappés par quelle couche ? Si vous vous trompez sur la portée, vous obtenez l'une de deux défaillances classiques : un paramètre de suivi qui se tronque silencieusement, ou une destination qui arrive sous la forme https%3A%2F%2Fexample.com et fait un 404. Cet article couvre les deux ensembles de caractères qui déterminent la réponse, les endroits où les règles changent, et comment vérifier ce qu'un lien transporte réellement. Pour une vue plus large de ce qu'une redirection fait de tout cela, voir types de redirections.

Les deux ensembles qui décident de tout

La RFC 3986 section 2.3 définit un ensemble non réservé qui n'a jamais besoin d'encodage : les lettres, les chiffres, et exactement quatre signes de ponctuation - trait d'union, point, tiret bas, tilde. Si votre valeur ne contient que ceux-là, vous n'avez rien à faire.

Tout le reste tombe dans l'une de deux catégories. Les caractères réservés portent une signification structurelle : : / ? # [ ] @ séparent les parties d'une URL, et ! $ & ' ( ) * + , ; = séparent des éléments à l'intérieur de ces parties. La section 2.2 les liste. Ils sont légaux en tant que syntaxe et doivent être encodés lorsqu'ils apparaissent comme donnée. Le reste, c'est tout ce qui se trouve hors ASCII, qui est encodé octet par octet après conversion en UTF-8 - ce qui explique pourquoi une lettre accentuée coûte généralement six caractères plutôt que trois.

Cela donne la seule règle qui vaille la peine d'être retenue : encodez un caractère quand il est une donnée et serait sinon lu comme de la syntaxe. Une esperluette entre deux paramètres est de la syntaxe. Une esperluette à l'intérieur d'un nom de campagne est une donnée, et si vous la laissez telle quelle, la liste de paramètres s'arrête là.

Une chaîne de requête où la valeur de campagne contient un espace et une esperluette, montrée correctement encodée à l'intérieur de la valeur et incorrectement sur l'URL entière

Encodez la valeur, pas l'URL

C'est l'erreur que je vois le plus souvent, et elle a toujours la même forme. Quelqu'un a une URL, sait qu'elle doit être encodée, alors il colle le tout dans un encodeur et obtient :

https%3A%2F%2Fexample.com%2Fspring%3Futm_campaign%3Dspring%20sale

Cette chaîne n'est pas une URL. C'est un texte en forme d'URL qui ne peut être qu'une valeur à l'intérieur d'une autre URL - ce qui est exactement sa place quand vous faites passer une destination à travers un redirecteur, et exactement ce qu'elle n'est pas quand vous essayez de l'ouvrir.

Le traitement correct encode chaque valeur séparément :

https://example.com/spring?utm_campaign=spring%20sale&utm_source=flyer

Le schéma, l'hôte, les séparateurs de chemin et les ? et & restent en tant que syntaxe. Seule la valeur a changé. Chaque langage fournit deux fonctions pour cette distinction, et choisir la mauvaise est l'autre moitié du problème : la page MDN sur encodeURIComponent dit sans détour que encodeURI laisse volontairement les caractères réservés tranquilles parce qu'elle s'attend à une URI entière, tandis que encodeURIComponent les échappe parce qu'elle s'attend à un fragment de celle-ci. Les valeurs veulent encodeURIComponent. En Python, c'est urllib.parse.quote, en Go url.QueryEscape, en PHP rawurlencode.

L'espace, c'est %20, sauf quand c'est un plus

Les deux sont corrects, à des endroits différents, et c'est la chose la plus déroutante de tout le sujet.

Dans un chemin ou une URI générique, un espace est %20. Dans une chaîne de requête construite comme le fait un formulaire HTML, un espace est +, car c'est ce que spécifie la sérialisation application/x-www-form-urlencoded dans le standard URL du WHATWG. Les deux formes sont lues comme un espace par tout analyseur de requête côté serveur que vous êtes susceptible de rencontrer.

Le piège, c'est le sens inverse. Si un signe plus est une donnée - un numéro de téléphone, un terme de recherche, une campagne nommée spring+summer - il doit s'écrire %2B. Laissé tel quel dans une chaîne de requête, il devient un espace, et vous passerez un après-midi à vous demander pourquoi le numéro dans votre CRM a perdu son indicatif pays.

CaractèreEncodéPourquoi c'est important
espace%20 ou ++ seulement à l'intérieur d'une chaîne de requête, %20 partout ailleurs
&%26Non encodée, la liste de paramètres s'arrête là
?%3FNon encodé, tout ce qui suit devient la requête
#%23Non encodé, le reste n'atteint jamais le serveur
+%2BNon encodé dans une requête, il arrive comme un espace
%%25Non encodé, les deux caractères suivants sont mangés

La ligne # mérite une remarque, car c'est celle qui produit le rapport de bug le plus déroutant. Un fragment n'est jamais envoyé au serveur. Mettez un # non encodé dans une cible de redirection et le serveur voit une URL tronquée alors que la barre d'adresse du navigateur a toujours l'air correcte, si bien que la personne qui le signale jure que le lien fonctionne.

Si vous construisez des URL de campagne à la main plus qu'occasionnellement, arrêtez : notre générateur d'UTM encode chaque valeur au fur et à mesure que vous tapez, et conventions de nommage UTM couvre le choix de valeurs qui n'ont besoin d'aucun encodage au départ. Raccourcissez le résultat sur votre propre domaine et le fouillis encodé cesse d'être quelque chose que quelqu'un doit regarder.

Le double encodage, et comment le repérer

Le double encodage, c'est ce qui se passe quand une valeur traverse deux couches qui font chacune leur travail. Le signe pourcentage est lui-même un caractère qui a besoin d'être échappé, si bien que %20 devient %2520, et %2520 devient %252520.

Les symptômes sont reconnaissables une fois qu'on les a vus. Un titre de page qui affiche spring%20sale à un vrai visiteur. Un paramètre qui arrive dans les analyses avec des séquences d'échappement visibles. Une redirection qui fonctionne au premier saut et échoue au second. La cause est presque toujours un appel d'encodage enveloppant une valeur déjà arrivée encodée, souvent parce qu'elle sortait d'une base de données qui stockait la forme encodée.

La solution consiste à décider quelle couche possède l'encodage et à laisser les autres s'en abstenir. Décodez une fois quand vous lisez une valeur, encodez une fois quand vous l'écrivez dans une URL, et ne faites jamais les deux dans la même fonction.

Une valeur traversant deux couches d'encodage si bien qu'un espace devient %20 puis %2520, avec le symptôme visible dans le navigateur

Où cela mord en pratique

Trois endroits, dans l'ordre où vous êtes susceptible de les rencontrer.

Les paramètres de suivi. Une valeur de campagne avec une esperluette non encodée tronque la liste de paramètres, si bien que la session arrive dans vos analyses comme trafic direct et que la campagne n'obtient aucun crédit. Rien ne produit d'erreur. Les paramètres UTM qui n'apparaissent pas dans GA4 couvre le diagnostic du côté des rapports, et les navigateurs suppriment les paramètres UTM couvre l'autre raison pour laquelle un paramètre peut disparaître entre le clic et la page.

Les redirections. Les règles serveur ré-encodent de manière incohérente, et le fait qu'une chaîne de requête survive ou non dépend de la directive utilisée. Une redirection 301 dans .htaccess a le tableau complet pour Apache ; en résumé, une règle qui remplace la chaîne de requête supprimera silencieusement la vôtre.

Les codes QR. L'encodage gonfle la longueur de la charge utile, et la longueur de la charge utile détermine la densité du code imprimé. Chaque espace coûte trois caractères au lieu d'un, chaque lettre accentuée six. Une URL de suivi avec quelques noms de campagne encodés peut faire grimper un code d'une version ou deux, ce qui fait une vraie différence à la taille d'une carte de visite - code QR qui ne scanne pas place la longueur de la charge utile parmi les quatre causes, exactement pour cette raison. Encoder un lien court plutôt que l'URL complète est la solution la moins coûteuse disponible.

Vérifiez ce qu'un lien transporte réellement

Deux commandes règlent presque tous les débats. La première montre ce que le serveur reçoit après une redirection :

curl -sI 'https://example.com/spring?utm_campaign=spring%20sale' | grep -i '^location'

La seconde construit l'encodage pour vous plutôt que de faire confiance à vos doigts, ce qui est utile quand une valeur contient plusieurs coupables à la fois :

curl -G --data-urlencode 'utm_campaign=spring & summer sale' \
  --data-urlencode 'utm_source=flyer' \
  -o /dev/null -w '%{url_effective}\n' https://example.com/spring

Lisez la sortie comme une donnée, pas comme une décoration. Si vous voyez %2520, vous avez un problème de double encodage ; si vous voyez une valeur qui se termine trop tôt, vous avez un séparateur non encodé ; et si vous voyez %3A%2F%2F au début, vous avez encodé l'URL entière. Notre vérificateur de liens fait la partie redirection dans un navigateur si vous préférez ne pas ouvrir de terminal.

L'habitude qui vaut la peine d'être prise, c'est de regarder l'URL finale une fois, à l'œil, avant qu'une campagne ne parte. Les bugs d'encodage sont invisibles dans un navigateur et évidents dans un terminal, et ils vous coûtent de l'attribution plutôt que de la disponibilité, ce qui explique pourquoi ils survivent si longtemps.

Lisez la série pilier

Cet article s'inscrit dans le cluster ingénierie. Pour le côté redirection, types de redirections couvre tous les codes de statut et les méthodes côté client, et comment fonctionnent les raccourcisseurs d'URL couvre ce qui se passe entre le clic et la page.

Sur le blog

Questions fréquentes

Qu'est-ce que l'encodage d'URL ?

Remplacer un caractère par un signe pourcentage suivi de sa valeur en octets en hexadécimal, afin que le caractère ne puisse pas être confondu avec de la syntaxe URL. Un espace devient %20, une esperluette devient %26, un point d'interrogation devient %3F. Le mécanisme est défini dans la RFC 3986 et est aussi appelé encodage en pourcentage.

Quels caractères doivent être encodés en URL ?

Tout ce qui se trouve hors de l'ensemble non réservé, que la RFC 3986 définit comme les lettres, les chiffres, et les quatre caractères trait d'union, point, tiret bas et tilde. Tout le reste est soit de la ponctuation réservée qui porte une signification structurelle, soit un octet hors ASCII, et les deux doivent être encodés en pourcentage lorsqu'ils apparaissent à l'intérieur d'une valeur plutôt que comme syntaxe.

Dois-je encoder l'URL entière ou seulement certaines parties ?

Seulement les parties. Faire passer une URL complète dans un encodeur transforme https://example.com en https%3A%2F%2Fexample.com, qui n'est plus du tout une URL. Encodez chaque valeur de paramètre de requête et chaque segment de chemin séparément, et laissez le schéma, l'hôte et les séparateurs tranquilles.

Un espace, c'est %20 ou un signe plus ?

Les deux, à des endroits différents. Dans un chemin et dans une URI générique, un espace est %20. Dans une chaîne de requête construite comme le fait un formulaire HTML, un espace est un signe plus, car c'est ce que spécifie la sérialisation application/x-www-form-urlencoded. Un signe plus littéral à l'intérieur d'une valeur de requête doit donc s'écrire %2B, sinon il sera lu comme un espace.

Qu'est-ce que le double encodage ?

Encoder quelque chose qui était déjà encodé, si bien que %20 devient %2520 parce que le signe pourcentage lui-même est échappé en %25. Le symptôme est une page qui affiche un %20 littéral dans son texte, ou un paramètre qui arrive avec des séquences d'échappement visibles. C'est presque toujours une valeur passée à travers deux couches qui l'ont chacune obligeamment encodée.

Pourquoi les caractères encodés rendent-ils un code QR plus difficile à scanner ?

Parce que chacun coûte trois caractères au lieu d'un seul. Un espace, c'est un caractère d'intention et trois de charge utile, donc une poignée d'entre eux peut faire grimper le code d'une version ou deux, ce qui signifie plus de modules dans la même surface imprimée. Encoder une longue URL de suivi dans un QR est l'un des moyens les plus rapides de fabriquer un code qui ne se scanne qu'à courte distance.

Essayer Elido

Collez une URL, obtenez un lien court

Sans inscription. Lien actif 30 jours. Inscrivez-vous pour le garder pour toujours.

Gratuit, sans inscription · 2 par jour

Essayer Elido

Raccourcisseur d'URL hébergé en UE : domaines personnalisés, analyses approfondies et API ouverte. Forfait gratuit - sans carte bancaire.

Tags
url encoding
percent encoding
encodeuricomponent
query string
utm parameters
url shortener

Lire la suite