Aller au contenu
OneKitly

Construire une URL avec paramètres qui survit à un copier-coller

Publié le 11/08/2026 · 12 min de lecture · Outils pour développeurs

Daniel Okonkwo

Daniel OkonkwoDéveloppeur front-end et rédacteur Tech chez OneKitly

Performance web · Formats de fichiers

Vérifié à partir de 4 sources

Voir le profil
En bref

Une chaîne de requête casse quand un caractère qui signifie quelque chose pour l'URL — une espace, une esperluette, un signe égal, un dièse — reste tel quel à l'intérieur d'une valeur. L'encodage pour cent règle cela, mais il existe trois encodages qui divergent, et la divergence se voit sur l'espace. encodeURIComponent écrit une espace %20 et laisse ! ' ( ) * ~ intacts. Le sérialiseur application/x-www-form-urlencoded, celui qu'utilisent URLSearchParams et tous les formulaires HTML, écrit une espace + et encode ! ' ( ) ~ en laissant * intact. La RFC 3986 stricte encode tout ce qui sort de A-Z a-z 0-9 - . _ ~, elle échappe donc aussi le * et garde le ~. Ce générateur propose les trois, et son mode formulaire a été comparé à URLSearchParams de Node sur dix-sept valeurs, dont un émoji, un saut de ligne et une chaîne accentuée : identique octet pour octet à chaque fois. Il double bien l'encodage : saisis %20 comme valeur et tu obtiens %2520. C'est correct — tu saisis une valeur brute, pas une valeur déjà encodée — et l'analyseur de l'outil la ramène au littéral %20. Le vrai bug est ailleurs. Donne-lui une URL de base contenant un fragment, https://example.com/page#section, et la chaîne est ajoutée après le fragment : l'analyseur d'URL du navigateur signale alors une partie requête vide et place tous les paramètres dans le hash, où aucun serveur ne les verra jamais. Son analyseur jette aussi le fragment, et une séquence héritée comme %E9 échoue au décodage, est conservée en texte littéral et ressort en %25E9 à la construction suivante.

Trois encodages, une différence visible : %20 ou +. Le mode formulaire du générateur reproduit URLSearchParams octet pour octet sur dix-sept valeurs — mais donne-lui une URL de base avec un fragment et tous les paramètres atterrissent dans le hash, invisibles du serveur.

Deux normes, et les navigateurs appliquent la plus récente

La RFC 3986 est le document le plus ancien et celui que tout le monde cite. Elle répartit les caractères entre non réservés — A-Z, a-z, 0-9 et les quatre signes - . _ ~ — et tout le reste, réservé ou à encoder en pour cent. C'est un modèle propre, et ce n'est pas celui qu'exécute ton navigateur. Les navigateurs appliquent le WHATWG URL Standard, un document vivant qui spécifie l'analyse et la sérialisation au moyen de plusieurs jeux d'encodage nommés, un par partie de l'URL. Les deux s'accordent sur les cas courants et divergent aux marges, et c'est aux marges qu'une URL cesse de fonctionner.

Le standard URL est explicite sur cette relation. Il indique que l'usage de son jeu d'encodage « component » avec l'UTF-8 donne des résultats identiques à encodeURIComponent de JavaScript. Et il définit le jeu application/x-www-form-urlencoded comme le jeu component augmenté de ! ' ( ) ~ — puis dit la même chose à l'envers, dans la phrase qui mérite d'être retenue : ce jeu contient tous les points de code sauf les caractères alphanumériques ASCII et * - . _ . Quatre caractères. Voilà toute la liste sûre d'un envoi de formulaire, et cela explique d'un coup d'œil pourquoi les deux modes divergent exactement sur ! ' ( ) ~ tout en laissant l'astérisque intact.

Le troisième mode du générateur, RFC 3986 stricte, est celui à prendre quand un serveur signe l'URL. Il reprend la sortie d'encodeURIComponent et échappe en plus ! ' ( ) * , en laissant le ~ intact, parce que le tilde est non réservé dans la RFC 3986 et que l'échapper changerait la signature. Les passerelles de paiement, les signatures OAuth 1.0 et certaines API d'entreprise plus anciennes calculent une empreinte sur la chaîne encodée : un encodeur qui laisse une apostrophe telle quelle produit donc une empreinte différente de la leur, et la requête est rejetée sans explication exploitable.

Oui il double l'encodage, et c'est la bonne réponse

Le double encodage est la plainte classique contre les générateurs d'URL, il a donc été testé directement. Saisis %20 dans un champ de valeur et la sortie est %2520. Saisis caf%C3%A9 et tu obtiens caf%25C3%25A9. Saisis un simple signe pour cent et tu obtiens %25. Ce n'est pas un bug, c'est la définition du champ. La case valeur contient la valeur saisie par ton utilisateur, et si cette valeur est réellement la suite de caractères c a f % 2 0, alors %2520 est le seul encodage qui la livrera.

Pour vérifier qu'un générateur est correct sur ce point, il ne faut pas regarder une sortie mais faire un aller-retour. Construis une URL, recolle-la dans le champ d'analyse de l'outil, reconstruis. Six valeurs ont subi ce cycle — une phrase accentuée avec une esperluette, un signe plus, un %20 littéral, un dièse dans une valeur, une valeur vide et un objet JSON — et les six sont revenues identiques à l'octet. Le %2520 collé s'est décodé en %20 littéral, qui s'est réencodé en %2520. Un générateur qui retirerait une couche pour faire propre échouerait précisément ici, en silence, sur la seule valeur qui compte.

Le bug : un fragment dans l'URL de base avale tous les paramètres

Le générateur raccorde sa chaîne à la base en cherchant un point d'interrogation : si la base en a déjà un, il ajoute avec une esperluette, sinon avec un point d'interrogation. Les deux branches ajoutent à la fin. Une URL ne fonctionne pas ainsi. L'ordre des parties est fixe — schéma, autorité, chemin, requête, fragment — et le fragment vient en dernier, si bien que tout ce qui s'écrit après lui lui appartient.

Mets la base à https://example.com/page#section, ajoute q et page, et l'outil affiche https://example.com/page#section?q=caf%C3%A9%20%26%20croissant&page=2. Ça a l'air correct. Passe cette chaîne à l'analyseur d'URL du navigateur et il rend un chemin /page, une partie requête vide, et un hash contenant #section?q=caf%C3%A9%20%26%20croissant&page=2 — tous les paramètres dans le fragment. Un fragment n'est jamais envoyé au serveur. La page se chargera, aucune erreur n'apparaîtra nulle part, et les paramètres n'existeront tout simplement pas du point de vue du serveur. La variante avec une requête déjà présente, https://example.com/page?a=1#frag, raconte la même histoire : la partie requête revient à ?a=1 et les nouveaux paramètres sont dans le hash.

L'analyse a le problème symétrique : elle coupe l'entrée au premier dièse et jette tout ce qui suit. Colle https://example.com/p?a=1#frag et la base revient en https://example.com/p, fragment disparu. Tu ne peux donc pas du tout utiliser l'outil sur une URL qui possède un fragment — il le perd à l'entrée et place mal la requête à la sortie. En attendant un correctif, la parade est de retirer le fragment toi-même, de construire la chaîne, puis de réassembler à la main : la base, puis la requête, puis le dièse, dans cet ordre.

Trois points plus petits à connaître avant de s'y fier

D'abord, une séquence pour cent qu'il ne sait pas décoder est conservée en texte littéral puis réencodée à la sortie. Colle une URL contenant a=%E9 — une séquence Latin-1 sur un octet, comme en émettent encore quantité de systèmes d'avant 2010 — et le décodeur lève une erreur, l'outil la rattrape et garde les trois caractères % E 9 comme valeur. Reconstruis et cela devient a=%25E9, une URL différente de celle que tu as collée. Rien ne te prévient. Le réflexe sûr est de vérifier tout paramètre qui revient en contenant un signe pour cent.

Ensuite, le générateur regroupe les paramètres par clé au lieu de conserver l'ordre de tes lignes. Les lignes a=1, b=2, a=3 ressortent en a=1&a=3&b=2 : les deux lignes a sont rapprochées et b descend. Pour un serveur web ordinaire c'est sans conséquence, presque rien ne se soucie de l'ordre des paramètres. Pour une requête signée, si, parce que la signature porte sur la chaîne exacte. Compare la sortie à l'ordre saisi dès que le destinataire calcule une empreinte sur la requête.

Enfin, l'option de tri des clés trie avec la comparaison sensible à la locale du navigateur, pas par point de code. Avec les clés b, a, B et _x elle produit _x, a, b, B. Un tri par point de code — celui que spécifie tout schéma de signature existant — produit B, _x, a, b. Les deux diffèrent dès que tes clés mêlent majuscules et minuscules ou commencent par un tiret bas, ce que font justement les noms de paramètres d'une API signée. Sers-toi de l'option pour rendre une longue URL lisible ; ne t'en sers pas pour la canoniser.

Deux points qu'il traite bien alors qu'il est facile de se tromper. Ses quatre syntaxes de valeurs répétées encodent toutes les crochets : a[]=1 s'écrit a%5B%5D=1 et a[0]=1 s'écrit a%5B0%5D=1, ce qui est correct au regard de la RFC 3986 où les crochets sont réservés aux littéraux d'hôte IPv6 — et ce que PHP, Rails et Express décodent tous. Et en mode virgule, la virgule entre les valeurs est encodée elle aussi : color=red,blue s'écrit color=red%2Cblue, ce qui est légal et sans danger avec tout serveur qui décode avant de découper.

La même valeur dans les trois encodages du générateur, tels qu'il les a réellement produits
Valeur saisieencodeURIComponentx-www-form-urlencodedRFC 3986 stricte
two wordstwo%20wordstwo+wordstwo%20words
café & croissantcaf%C3%A9%20%26%20croissantcaf%C3%A9+%26+croissantcaf%C3%A9%20%26%20croissant
C'est (chouette) !C'est%20(chouette)%20!C%27est+%28chouette%29+%21C%27est%20%28chouette%29%20%21
~tilde*star~tilde*star%7Etilde*star~tilde%2Astar
a=b&c#da%3Db%26c%23da%3Db%26c%23da%3Db%26c%23d
%20 saisi littéralement%2520%2520%2520
Générateur de chaîne de requête URLConstruis une chaîne de requête URL à partir de paires clé/valeur, avec encodage correct, syntaxes de tableau et préréglage UTM — ou analyse une URL existante.Essayer l'outil

Questions fréquentes

Quel encodage choisir si je ne sais pas ce qu'attend le serveur ?
Prends encodeURIComponent, le mode par défaut. Une espace devient %20, que tout serveur décode correctement, tandis que le + n'est interprété comme une espace que par du code qui sait lire des données de formulaire. C'est toute la raison de préférer %20 dans une URL destinée à être collée dans un e-mail, une messagerie ou un tableur : elle survit à une lecture par quelque chose qui ignore qu'elle vient d'un formulaire. Passe à x-www-form-urlencoded seulement pour reproduire ce qu'enverrait un formulaire de navigateur — par exemple pour comprendre pourquoi un envoi de formulaire diffère de ton URL construite à la main. Passe à la RFC 3986 stricte quand un serveur signe ou hache la chaîne de requête, parce que les échappements supplémentaires de ! ' ( ) * sont ceux que produisent la plupart des bibliothèques de signature.
Pourquoi mes paramètres ont-ils disparu quand le lien se terminait par #section ?
Parce qu'ils ont été écrits après le fragment, et que tout ce qui suit un dièse est le fragment. Ce générateur ajoute la chaîne à la fin de ce que tu lui as donné comme base : une base https://example.com/page#section produit donc https://example.com/page#section?q=x, et le navigateur lit toute la fin comme un seul fragment. Un fragment est résolu entièrement côté client ; il ne fait pas partie de la ligne de requête et aucun serveur ne le voit. Répare à la main : prends la base sans son fragment, ajoute la chaîne, puis remets le fragment tout à la fin, ce qui donne https://example.com/page?q=x#section. Si tu recolles cette URL corrigée dans l'outil, il analysera bien la requête mais perdra de nouveau le fragment : garde donc le fragment ailleurs pendant que tu travailles.
Comment envoyer une liste de valeurs pour le même paramètre ?
Il n'y a pas de norme, et c'est pourquoi l'outil propose quatre syntaxes. Répéter la clé, color=red&color=blue, c'est ce que produit un formulaire HTML quand plusieurs cases partagent un nom, et ce que renvoie URLSearchParams.getAll ; c'est le choix par défaut le plus sûr. Les crochets, color[]=red&color[]=blue, sont une convention PHP que lisent aussi Rails et plusieurs cadres PHP. Les crochets indexés, color[0]=red, conservent la position et servent là où le serveur reconstruit un tableau ordonné. Une valeur jointe par virgules, color=red,blue, est un seul paramètre d'une seule valeur que le serveur découpe lui-même. Choisis celle que ton serveur documente ; s'il n'en documente aucune, répète la clé. Note que l'outil encode les crochets et la virgule, ce qui est légal et que chacun de ces cadres décode avant d'analyser.
Est-il prudent de mettre une adresse e-mail ou un numéro de commande dans une chaîne de requête ?
Techniquement cela fonctionnera, et il faut quand même l'éviter. Une chaîne de requête fait partie de l'URL, et les URL sont écrites dans l'historique du navigateur, dans les journaux d'accès du serveur, dans ceux des proxys et des CDN, et dans l'en-tête Referer envoyé à tout script tiers chargé par la page. Toute donnée personnelle que tu y places est recopiée dans tous ces endroits par des systèmes à qui personne n'a dit qu'elle était personnelle, et elle y reste pour la durée de conservation propre à chacun. Mets sans hésiter des identifiants dans la chaîne — un identifiant de produit, un numéro de page, un nom de campagne — et mets tout ce qui identifie une personne dans le corps d'une requête POST, ou derrière un jeton opaque court que ton propre serveur résout. Idem pour tout ce qui donne un accès : un jeton dans une URL est un jeton dans un fichier de journal.
Jusqu'où une URL peut-elle aller avant que quelque chose la tronque ?
Aucune spécification ne fixe de limite ; chaque implémentation fixe la sienne, et celle qui frappe en premier n'est généralement pas le navigateur. La RFC 3986 refuse explicitement d'imposer un maximum et recommande plutôt que tout ce qui manipule des URL sache encaisser des longueurs supérieures à ce qu'il attend. En pratique, les navigateurs acceptent des dizaines de milliers de caractères, tandis que serveurs web et proxys refusent couramment une ligne de requête au-delà d'environ 8 kilo-octets et répondent par un statut 414. Les outils d'analyse et de résultats de recherche tronquent bien plus tôt. Le conseil pratique ne dépend d'aucun chiffre exact : si ta chaîne de requête atteint des milliers de caractères, les données ont leur place dans un corps de requête POST ou derrière un identifiant court, pas dans un lien. Les URL longues cassent aussi au collage dans les clients de messagerie et les applications de discussion, qui les coupent à une colonne et transforment un lien en deux.

Articles qui pourraient t'intéresser

Tous les guides
ExplicationQu'est-ce qu'un QR code ?Un QR code est un code-barres 2D qu'une caméra lit pour ouvrir un lien ou du texte. Voici ce qu'il est, pourquoi il contient tant, son anatomie, et une note de sécurité.ExplicationQu'est-ce qu'une expression cron ?Une expression cron planifie l'exécution automatique d'une tâche à des moments définis. Voici à quoi elle sert, ses cinq champs, comment la lire, et les pièges courants.GuideL'encodage d'URL expliqué : le pourcentage et là où ça coinceL'encodage par pourcentage se décide composant par composant, et c'est de là que vient toute la confusion. Une barre oblique est légale dans un chemin et doit être échappée dans une valeur de requête ; une espace est %20 dans un chemin et peut être + dans un corps de formulaire. Voici les ensembles exacts de la RFC 3986, les trois fonctions JavaScript qui divergent, et les pièges.ExplicationExtraire toutes les adresses e-mail ou URL d'un bloc de texteUne URL en fin de phrase garde le point ; une adresse e-mail en fin de la même phrase, non. Un prénom accentué dans une adresse revient tronqué. Chaque cas a été passé dans les outils et la sortie exacte est reproduite.ExplicationCe qu'il y a dans un JWT — et ce qu'il ne protège pasUn JWT est signé, pas chiffré. Quiconque détient le jeton peut décoder la charge utile et lire chaque revendication. Voici un vrai jeton, décodé sans aucune clé, plus les trois attaques que la signature est censée arrêter et le seul problème qu'elle ne peut pas résoudre.ComparatifJSON vs XML : quelle différence ?JSON et XML stockent tous deux des données structurées en texte, mais avec des compromis différents. Voici à quoi ressemble chacun, où chacun l'emporte et comment choisir.

Outils similaires

Ces chiffres viennent de l'exécution de ces outils sur de vrais fichiers, puis de la compression du résultat, pas d'une promesse d'éditeur. Le poids dépend entièrement du fichier : une feuille de style écrite avec de longs commentaires ne se comprime pas comme une feuille sans commentaires, et tes chiffres ne seront pas les nôtres. Les minifieurs ne se valent pas non plus — deux outils de ce site divergent sur la même entrée — alors traite toute sortie minifiée comme du code neuf, à relire avant mise en ligne. Garde l'original lisible dans le gestionnaire de versions, minifie à la compilation, et vérifie la page dans un navigateur avant de publier.

Sources

Tu as repéré une erreur dans cet article ?