URL-encoding vervangt een teken door een procentteken en twee hexadecimale cijfers: een spatie wordt %20, een ampersand wordt %26, een vraagteken wordt %3F. Het doel is te voorkomen dat een teken wordt gelezen als URL-syntax terwijl je het als data bedoelde. Niets meer.
De reden dat het moeilijker aanvoelt, is dat bijna elke vraag hierover in feite een vraag is over scope. Welke tekens, in welk deel van de URL, geëscaped door welke laag? Krijg je de scope verkeerd, dan krijg je een van twee klassieke mislukkingen: een trackingparameter die stilletjes wordt afgekapt, of een bestemming die aankomt als https%3A%2F%2Fexample.com en een 404 oplevert. Dit artikel behandelt de twee tekensets die het antwoord bepalen, de plekken waar de regels veranderen, en hoe je controleert wat een link daadwerkelijk bevat. Voor het bredere plaatje van wat een redirect hiermee doet, zie soorten redirects.
De twee sets die alles bepalen
RFC 3986, sectie 2.3 definieert een unreserved set die nooit encoding nodig heeft: letters, cijfers, en precies vier leestekens - koppelteken, punt, underscore, tilde. Bevat je waarde alleen die, dan hoef je niets te doen.
Al het andere valt in een van twee categorieën. Reserved tekens dragen structurele betekenis: : / ? # [ ] @ scheiden de delen van een URL, en ! $ & ' ( ) * + , ; = scheiden dingen binnen die delen. Sectie 2.2 vermeldt ze. Ze zijn legaal als syntax en moeten worden gecodeerd wanneer ze als data voorkomen. De rest is alles buiten ASCII, dat byte voor byte wordt gecodeerd na conversie naar UTF-8 - wat verklaart waarom één letter met accent doorgaans zes tekens kost in plaats van drie.
Dat levert de enige regel op die het waard is om te onthouden: codeer een teken wanneer het data is en anders als syntax zou worden gelezen. Een ampersand tussen twee parameters is syntax. Een ampersand binnen een campagnenaam is data, en als je hem ongemoeid laat, eindigt de parameterlijst daar.
Codeer de waarde, niet de URL
Dit is de fout die ik het vaakst zie, en hij heeft altijd dezelfde vorm. Iemand heeft een URL, weet dat die encoding nodig heeft, plakt het hele ding in een encoder en krijgt:
https%3A%2F%2Fexample.com%2Fspring%3Futm_campaign%3Dspring%20sale
Die string is geen URL. Het is een stuk tekst in de vorm van een URL dat alleen ooit een waarde binnen een andere URL kan zijn - precies waar het thuishoort wanneer je een bestemming doorgeeft via een redirector, en precies waar het niet thuishoort wanneer je hem probeert te openen.
De juiste behandeling codeert elke waarde afzonderlijk:
https://example.com/spring?utm_campaign=spring%20sale&utm_source=flyer
Scheme, host, padscheidingstekens en de ? en & blijven als syntax staan. Alleen de waarde is veranderd. Elke taal levert twee functies voor dit onderscheid, en de verkeerde kiezen is de andere helft van het probleem: MDN's pagina over encodeURIComponent is er duidelijk over dat encodeURI gereserveerde tekens bewust ongemoeid laat omdat het een hele URI verwacht, terwijl encodeURIComponent ze escaped omdat het een fragment daarvan verwacht. Waarden willen encodeURIComponent. In Python is dat urllib.parse.quote, in Go url.QueryEscape, in PHP rawurlencode.
Een spatie is %20, behalve waar het een plus is
Allebei correct, op verschillende plekken, en dit is het meest verwarrende aspect van het hele onderwerp.
In een pad of een generieke URI is een spatie %20. In een querystring die is opgebouwd zoals een HTML-formulier dat doet, is een spatie +, omdat dat is wat de application/x-www-form-urlencoded-serialisatie in de WHATWG URL-standaard voorschrijft. Beide vormen worden door elke server-side queryparser die je waarschijnlijk tegenkomt, gelezen als een spatie.
De valkuil is de omgekeerde richting. Als een plusteken data is - een telefoonnummer, een zoekterm, een campagne genaamd spring+summer - moet het worden geschreven als %2B. Ongemoeid gelaten in een querystring wordt het een spatie, en dan besteed je een middag aan uitzoeken waarom het nummer in je CRM zijn landcode kwijt is.
| Teken | Gecodeerd | Waarom het ertoe doet |
|---|---|---|
| spatie | %20 of + | + alleen binnen een querystring, %20 overal |
& | %26 | Niet gecodeerd, eindigt de parameterlijst daar |
? | %3F | Niet gecodeerd, wordt alles erna de query |
# | %23 | Niet gecodeerd, de rest bereikt de server helemaal niet |
+ | %2B | Niet gecodeerd in een query, komt aan als een spatie |
% | %25 | Niet gecodeerd, de volgende twee tekens worden opgegeten |
De #-rij verdient een toelichting, omdat die de meest verwarrende bugmelding oplevert. Een fragment wordt nooit naar de server verzonden. Zet een niet-gecodeerde # in een redirectbestemming en de server ziet een afgekapte URL, terwijl de adresbalk van de browser er nog gewoon goed uitziet, waardoor degene die het meldt, zweert dat de link klopt.
Bouw je campagne-URL's vaker dan af en toe met de hand, stop daarmee: onze UTM-builder codeert elke waarde terwijl je typt, en UTM-naamconventies behandelt het kiezen van waarden die van meet af aan geen encoding nodig hebben. Verkort het resultaat op je eigen domein en de gecodeerde puinhoop is niet langer iets waar iemand naar hoeft te kijken.
Dubbele encoding, en hoe je het herkent
Dubbele encoding is wat er gebeurt wanneer een waarde door twee lagen gaat die elk hun werk doen. Het procentteken is zelf een teken dat geëscaped moet worden, zodat %20 %2520 wordt, en %2520 %252520 wordt.
De symptomen zijn herkenbaar zodra je ze een keer hebt gezien. Een paginatitel die spring%20sale toont aan een echte bezoeker. Een parameter die in analytics aankomt met zichtbare escape-sequenties. Een redirect die werkt bij de eerste hop en faalt bij de tweede. De oorzaak is bijna altijd een encode-aanroep die om een waarde is gewikkeld die al gecodeerd aankwam, vaak omdat die uit een database kwam die de gecodeerde vorm had opgeslagen.
De oplossing is te beslissen welke laag de encoding beheert en de andere lagen er met hun handen vanaf te houden. Decodeer één keer wanneer je een waarde leest, codeer één keer wanneer je hem in een URL schrijft, en doe nooit beide in dezelfde functie.
Waar dit in de praktijk pijn doet
Drie plekken, in de volgorde waarin je ze waarschijnlijk tegenkomt.
Trackingparameters. Een campagnewaarde met een niet-gecodeerde ampersand kapt de parameterlijst af, waardoor de sessie in je analytics als direct traffic terechtkomt en de campagne geen credit krijgt. Er verschijnt geen enkele foutmelding. UTM-parameters niet zichtbaar in GA4 behandelt de diagnose vanaf de rapportagekant, en browsers verwijderen UTM-parameters behandelt de andere reden waarom een parameter kan verdwijnen tussen klik en pagina.
Redirects. Serverregels coderen inconsistent opnieuw, en of een querystring het überhaupt overleeft, hangt af van de gebruikte directive. Een 301-redirect in .htaccess heeft de volledige tabel voor Apache; de korte versie is dat een regel die de querystring vervangt, de jouwe stilletjes laat vallen.
QR-codes. Encoding blaast de payloadlengte op, en de payloadlengte bepaalt hoe dicht de geprinte code is. Elke spatie kost drie tekens in plaats van één, elke letter met accent zes. Een tracking-URL met een paar gecodeerde campagnenamen kan een code een versie of twee omhoog duwen, en dat is een echt verschil op visitekaartjesformaat - QR-code scant niet noemt payloadlengte precies daarom als een van de vier oorzaken. Een korte link coderen in plaats van de volledige URL is de goedkoopste beschikbare oplossing.
Controleer wat een link daadwerkelijk bevat
Twee commando's beslechten bijna elke discussie. Het eerste toont wat de server ontvangt na een redirect:
curl -sI 'https://example.com/spring?utm_campaign=spring%20sale' | grep -i '^location'
Het tweede bouwt de encoding voor je op in plaats van op je vingers te vertrouwen, wat handig is wanneer een waarde meteen meerdere boosdoeners bevat:
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
Lees de output als data, niet als versiering. Zie je %2520, dan heb je een dubbele-encodingprobleem; zie je een waarde die vroegtijdig eindigt, dan heb je een niet-gecodeerd scheidingsteken; en zie je %3A%2F%2F aan het begin, dan heb je de hele URL gecodeerd. Onze linkchecker doet de redirecthelft in een browser als je liever geen terminal opent.
De gewoonte die het waard is om aan te leren, is de uiteindelijke URL één keer met het blote oog te bekijken voordat een campagne live gaat. Encodingbugs zijn onzichtbaar in een browser en overduidelijk in een terminal, en ze kosten je attributie in plaats van uptime, wat verklaart waarom ze zo lang overleven.
Lees de cornerstone-serie
Dit artikel valt onder de engineering-cluster. Voor de redirectkant behandelt soorten redirects elke statuscode en client-side methode, en hoe werken URL-verkorters behandelt wat er gebeurt tussen klik en pagina.
Gerelateerd op de blog
Veelgestelde vragen
Wat is URL-encoding?
Een teken vervangen door een procentteken gevolgd door zijn bytewaarde in hexadecimaal, zodat het teken niet kan worden verward met URL-syntax. Een spatie wordt %20, een ampersand wordt %26, een vraagteken wordt %3F. Het mechanisme is vastgelegd in RFC 3986 en wordt ook percent-encoding genoemd.
Welke tekens moeten URL-encoded worden?
Alles buiten de unreserved set, die RFC 3986 definieert als letters, cijfers, en de vier tekens koppelteken, punt, underscore en tilde. Al het andere is ofwel gereserveerde interpunctie die structurele betekenis draagt, ofwel een byte buiten ASCII, en beide moeten percent-encoded worden wanneer ze binnen een waarde voorkomen in plaats van als syntax.
Moet ik de hele URL encoderen of alleen delen ervan?
Alleen de delen. Als je een volledige URL door een encoder haalt, wordt https://example.com https%3A%2F%2Fexample.com, en dat is helemaal geen URL meer. Encodeer elke queryparameterwaarde en elk padsegment apart, en laat het scheme, de host en de scheidingstekens ongemoeid.
Is een spatie %20 of een plusteken?
Allebei, op verschillende plekken. In een pad en in een generieke URI is een spatie %20. In een querystring die is opgebouwd zoals HTML-formulieren dat doen, is een spatie een plusteken, omdat dat is wat de application/x-www-form-urlencoded-serialisatie voorschrijft. Een letterlijk plusteken binnen een queryparameterwaarde moet daarom worden geschreven als %2B, anders wordt het gelezen als een spatie.
Wat is dubbele encoding?
Iets encoderen dat al gecodeerd was, zodat %20 %2520 wordt omdat het procentteken zelf wordt geëscaped tot %25. Het symptoom is een pagina die letterlijk %20 in de tekst toont, of een parameter die aankomt met zichtbare escape-sequenties. Het is bijna altijd een waarde die door twee lagen gaat die hem elk vriendelijk hebben gecodeerd.
Waarom maken gecodeerde tekens een QR-code moeilijker te scannen?
Omdat elk teken drie tekens kost in plaats van één. Een spatie is één teken aan bedoeling en drie aan payload, dus een handvol daarvan kan de code een versie of twee hoger duwen, wat meer modules betekent binnen dezelfde geprinte oppervlakte. Een lange tracking-URL coderen in een QR-code is een van de snelste manieren om een code te maken die alleen van dichtbij scant.
Probeer Elido
Plak een URL, krijg een werkende korte link
Geen aanmelding nodig. Link blijft 30 dagen actief. Meld je aan om hem voor altijd te bewaren.
Gratis, geen aanmelding nodig · 2 per dag