Wedden dat je al eens een bug hebt gehad waarbij een URL met spaties of speciale tekens iets brak? Misschien veroorzaakte een zoekopdracht met & vreemd gedrag. Of je zag %20 in een URL en vroeg je af wat dat was. Laten we dit eens en voor altijd ophelderen.
Waarom URLs codering nodig hebben
URLs mogen slechts een beperkte set tekens bevatten. De RFC 3986-specificatie definieert "niet-gereserveerde" tekens die altijd veilig zijn: A-Z, a-z, 0-9, -, _, . en ~. Al het andere — spaties, &, =, ?, niet-ASCII tekens zoals ñ of 日本語 — moet procentgecodeerd worden.
Hoe procentcodering werkt
Het is vrij eenvoudig: neem de UTF-8-bytewaarde van het teken en stel elke byte voor als % gevolgd door twee hexadecimale cijfers.
Voorbeelden:
- Spatie →
%20 &→%26=→%3Dé→%C3%A9(twee bytes in UTF-8)日→%E6%97%A5(drie bytes in UTF-8)
Dus Hello World wordt Hello%20World in een URL-pad.
De #1 bug: dubbele codering
Dit is de meest voorkomende URL-coderingsfout die ik tegenkom, en het is subtiel. Je codeert een string, geeft die door aan een functie die het opnieuw codeert. Nu wordt %20 (een al gecodeerde spatie) %2520 omdat het %-teken zelf wordt gecodeerd.
Voorbeeld van het probleem:
De oplossing: waarden één keer coderen, op het juiste niveau. Codeer niets dat al gecodeerd is.
Plusteken vs. %20 — Ja, het is verwarrend
In URL query strings kunnen spaties + of %20 zijn. De +-conventie komt van HTML-formuliercodering (application/x-www-form-urlencoded). In URL-paden is alleen %20 geldig.
Dus https://example.com/hello world → pad wordt gecodeerd als https://example.com/hello%20world
Maar https://example.com/search?q=hello world → query kan ?q=hello+world of ?q=hello%20world zijn
encodeURI vs. encodeURIComponent
JavaScript geeft je twee functies, en de verkeerde gebruiken is een klassieke fout:
encodeURI()— Codeert een volledige URL. Laat:,/,?,#,&,=ongemoeid omdat ze structurele delen van de URL zijn.encodeURIComponent()— Codeert een URL-component (zoals een queryparameterwaarde). Codeert WEL:,/,?,#,&,=omdat die onderdeel van de data kunnen zijn.
Vuistregel: gebruik encodeURIComponent() voor waarden, nooit voor volledige URLs. De MDN-documentatie legt het verschil prachtig uit.
Andere talen
- Python:
urllib.parse.quote()voor paden,urllib.parse.urlencode()voor query strings — documentatie hier - Java:
URLEncoder.encode()(gebruikt+voor spaties — let op!) - PHP:
urlencode()voor query strings,rawurlencode()voor paden
Snelle tips
- Codeer altijd parameter-waarden, nooit volledige URLs
- Decodeer één keer aan de ontvangende kant — geef gecodeerde waarden niet door meerdere lagen door
- Test met randgevallen: spaties,
&,=,#, niet-ASCII tekens en emoji
Veilig URLs bouwen in JavaScript
De juiste manier om URLs met queryparameters te bouwen is met de ingebouwde URL en URLSearchParams API's. Zij regelen alle codering voor je:
Merk op hoe URLSearchParams + gebruikt voor spaties (HTML-formuliercoderingsconventie) en de & in de waarde correct codeert zodat het niet verward wordt met de & die parameters scheidt. Dit is veel veiliger dan stringconcatenatie.
Veelvoorkomende URL-coderingsvalkuilen
1. De hele URL coderen in plaats van alleen waarden. Als je encodeURIComponent() op een volledige URL toepast, codeert het ://, /, ? en & — waardoor de URL volledig onbruikbaar wordt. Codeer alleen individuele parameterwaarden.
2. Vergeten hash-fragmenten te coderen. Het #-teken in een URL start een fragment-identifier. Als je data een # bevat en je codeert het niet, verdwijnt alles erna vanuit het perspectief van de server. De server ziet nooit fragment-identifiers — ze zijn alleen client-side.
3. Internationale domeinnamen (IDN) niet afhandelen. Domeinnamen met niet-ASCII tekens zoals example.日本 hebben speciale behandeling nodig genaamd Punycode-codering. Dit staat los van procentcodering en geldt alleen voor het hostnaamdeel van de URL.
4. Spaties inconsistent coderen. Sommige delen van je systeem coderen spaties als + en andere als %20. Dit werkt meestal prima voor decodering, maar kan problemen veroorzaken bij URL-vergelijking en caching. Kies één conventie en houd je eraan.
Procentcodering snelreferentie
| Teken | Gecodeerd | Waarom het gecodeerd moet worden |
| Spatie | %20 of + | Niet toegestaan in URLs |
& | %26 | Scheidt queryparameters |
= | %3D | Scheidt sleutel van waarde |
? | %3F | Start de query string |
# | %23 | Start de fragment-identifier |
% | %25 | Het escape-teken zelf |
/ | %2F | Padscheidingsteken |
@ | %40 | Gebruikt in het userinfo-gedeelte |
URL-codering in verschillende contexten
URL-codering is niet alleen voor browser-URLs. Je komt het tegen in deze veelvoorkomende situaties:
- API-verzoeken: Queryparameters in REST API-aanroepen moeten correct gecodeerd worden, vooral als ze gebruikersinvoer bevatten
- Redirect-URLs: Als je een return-URL doorgeeft als parameter (zoals
?redirect=https://...), moet de hele redirect-URL als waarde gecodeerd worden - OAuth-flows: OAuth callback-URLs en state-parameters zijn berucht lastig omdat ze meerdere lagen van URL-codering bevatten
- Deep links: Mobiele deep links volgen dezelfde URL-coderingsregels, maar sommige platformen hebben extra vereisten
URL-coderingsproblemen debuggen
Als er iets misgaat met URL-codering, hier is mijn debug-checklist:
1. Controleer op dubbele codering — Zoek naar %25 in de URL, wat betekent dat een % twee keer gecodeerd is
2. Bekijk het ruwe verzoek — Gebruik het Network-tabblad in de DevTools van je browser om precies te zien welke URL verstuurd werd, voordat de browser de gedecodeerde versie toont
3. Vergelijk gecodeerd vs. gedecodeerd — Plak de problematische URL in een decoder om te zien wat het daadwerkelijk bevat
4. Controleer server-side decodering — Sommige frameworks decoderen URL-parameters automatisch, en het handmatig daarbovenop doen veroorzaakt problemen
De anatomie van een URL
Oké, laten we even een stap terug doen. Voordat we dieper in codering duiken, moet je echt begrijpen waaruit een URL is opgebouwd. Ik weet het, ik weet het — je gebruikt URLs je hele carrière al. Maar ik wed dat je niet alle onderdelen bij hun officiële namen kent. De meeste ontwikkelaars niet, en daar begint de verwarring over codering.
Volgens RFC 3986 heeft een URL deze structuur:
Laat me je door elk onderdeel leiden:
- Scheme (
https,ftp,mailto) — Het protocol. Geen codering nodig hier, het zijn altijd ASCII-letters. - Authority — Dit omvat de optionele
user:password@(die je in 2026 eigenlijk nooit zou moeten gebruiken), de host (domeinnaam of IP) en een optioneel poortnummer. - Path (
/search/results) — Het hiërarchische deel. Forward slashes/scheiden segmenten. Binnen elk segment moet je speciale tekens coderen, maar je codeert NIET de slashes zelf. - Query (
?q=hello&lang=en) — Sleutel-waardeparen na de?. De&scheidt paren,=scheidt sleutels van waarden. Je codeert de sleutels en waarden, maar niet de structurele&en=. - Fragment (
#section-2) — Het deel na#. Deze is interessant — het wordt nooit naar de server gestuurd. Het is puur client-side. Maar je moet nog steeds speciale tekens erin coderen.
Dit is wat mensen in de war brengt: verschillende delen van de URL hebben verschillende coderingsregels. Een / is prima in het pad (het is een scheidingsteken!) maar moet als %2F gecodeerd worden als het in een queryparameterwaarde voorkomt. Een @-teken is prima in het authority-gedeelte maar moet gecodeerd worden in het pad. Daarom veroorzaken one-size-fits-all coderingsfuncties zoveel bugs.
Zie het zo: de URL is een zin met grammaticaregels. De speciale tekens zijn leestekens. Je zou een komma die daadwerkelijk als komma gebruikt wordt niet coderen — je codeert alleen een komma die deel uitmaakt van de data en verward zou kunnen worden met leestekens.
encodeURI vs. encodeURIComponent: Het JavaScript-mijnenveld
Oké, dit onderwerp maakt me absoluut gek want ik zie ontwikkelaars het CONSTANT verkeerd doen. JavaScript geeft je twee coderingsfuncties, en ze klinken bijna identiek, maar de verkeerde gebruiken verpest je dag.
Laat me het duidelijk uitleggen:
encodeURI() is ontworpen voor het coderen van een complete URL. Het codeert onveilige tekens maar laat de structurele met rust — dingen als :, /, ?, #, &, =, @. Want als je een volledige URL codeert, wil je natuurlijk de URL-structuur niet kapotmaken.
encodeURIComponent() is ontworpen voor het coderen van een enkele waarde die in een URL gaat. Het codeert ALLES behalve letters, cijfers en - _ . ~. Dit omvat :, /, ?, #, &, = — want wanneer deze in een waarde voorkomen, zijn het data, geen structuur.
Hier is een vergelijking die het kristalhelder maakt:
| Teken | encodeURI() | encodeURIComponent() |
: | : (ongewijzigd) | %3A |
/ | / (ongewijzigd) | %2F |
? | ? (ongewijzigd) | %3F |
# | # (ongewijzigd) | %23 |
& | & (ongewijzigd) | %26 |
= | = (ongewijzigd) | %3D |
@ | @ (ongewijzigd) | %40 |
| Spatie | %20 | %20 |
é | %C3%A9 | %C3%A9 |
Nu wordt het eng. Kijk wat er gebeurt als je de verkeerde functie gebruikt:
En de omgekeerde fout is net zo erg:
De gouden regel: encodeURIComponent voor waarden, encodeURI voor volledige URLs. Of beter nog, gebruik gewoon de URL API en laat de browser het afhandelen. Serieus, de URL en URLSearchParams klassen bestaan met een reden. Bekijk de MDN encodeURIComponent docs en MDN encodeURI docs voor alle details.
URL-codering in verschillende talen
JavaScript is niet de enige taal met verwarrende URL-coderingsfuncties. Elke taal heeft zijn eigen eigenaardigheden, en ik meen het, sommige zijn nog verwarrender dan het paar van JavaScript.
Python — Eigenlijk best redelijk, als je de juiste module vindt:
Java — Hier wordt het vreemd. URLEncoder is ontworpen voor HTML-formuliercodering, niet voor algemene URL-codering. Dus spaties worden + in plaats van %20:
C# — .NET geeft je eigenlijk de juiste tools, maar er zijn zo'n vijf verschillende methoden die allemaal net iets anders doen:
PHP — Oh PHP. Natuurlijk heb je twee functies met bijna identieke namen die net iets anders doen:
Go — Schoon en verstandig, zoals Go dat wil zijn:
De conclusie? Elke taal behandelt het + vs %20 ding anders, en elke taal heeft minstens één functie die je zal verrassen. Controleer altijd de documentatie van de taal waarmee je werkt. Ga er niet van uit dat het hetzelfde werkt als JavaScript.
Unicode in URLs: Het Wilde Westen
Oké, hier wordt het PAS ECHT interessant. Wat gebeurt er als je niet-ASCII tekens in een URL zet? Wat als je een URL wilt met Japans, Arabisch of — ik maak geen grapje — emoji?
Het antwoord betreft twee compleet verschillende systemen, en ze door elkaar halen is een klassieke fout.
Voor het pad en querygedeelte: Je gebruikt procentcodering. Neem de UTF-8 bytes van het teken en codeer elk. Dus café wordt caf%C3%A9. Je browser doet dit automatisch en toont meestal de mooie versie in de adresbalk, maar onder de motorkap verstuurt het de procentgecodeerde versie.
Voor domeinnamen: Procentcodering wordt NIET gebruikt. In plaats daarvan is er een systeem genaamd Punycode. Het converteert Unicode-domeinnamen naar ASCII-compatibele strings die beginnen met xn--.
Kijk hier eens naar:
café.com→xn--caf-dma.commünchen.de→xn--mnchen-3ya.de例え.jp→xn--r8jz45g.jp
Waarom twee verschillende systemen? Omdat DNS (het systeem dat domeinnamen omzet in IP-adressen) in de jaren '80 gebouwd is en alleen ASCII ondersteunt. Dus moesten ze een manier bedenken om Unicode in ASCII-strings te proppen — en Punycode is die uitvinding. De pad- en querygedeelten van een URL worden daarentegen afgehandeld door webservers die met procentgecodeerde bytes kunnen omgaan.
Er is trouwens een hele specificatie voor Unicode in URLs genaamd IRI (Internationalized Resource Identifiers) gedefinieerd in RFC 3987. Een IRI is in feite een URL die Unicode-tekens direct toestaat. Browsers converteren IRI's achter de schermen naar URI's.
En ja, emoji-domeinen bestaan. 💩.la is een echt domein (of was dat ooit). Het wordt met Punycode gecodeerd tot xn--ls8h.la. Ik raad emoji-domeinen niet aan voor iets serieus, maar het is een leuk bewijs dat het systeem werkt.
Eén valkuil bij Unicode in URLs: verschillende Unicode-representaties van "hetzelfde" teken. Bijvoorbeeld, é kan worden weergegeven als een enkel codepunt (U+00E9) of als e + een combinerend accent (U+0065 + U+0301). Deze produceren verschillende procentgecodeerde strings! De WHATWG URL Standard beveelt NFC-normalisatie aan, maar niet alle systemen volgen dit consistent.
Dubbele codering: De bug die je dromen achtervolgt
Ik noemde dubbele codering eerder, maar het verdient een eigen diepgaande behandeling omdat deze bug waarschijnlijk meer collectieve ontwikkelaarsuuren heeft verspild dan elk ander URL-gerelateerd probleem. Ik heb het meegemaakt — meerdere keren.
Hier is het basisscenario:
Wat is er gebeurd? Als je hello%20world een tweede keer codeert, wordt het %-teken gecodeerd tot %25. Dus %20 wordt %2520. De server ziet de letterlijke string %20 in plaats van een spatie.
Dit klinkt voor de hand liggend als ik het zo uitleg, maar in echte codebases is het ongelooflijk sluipend. Hier zijn de scenario's waarin dubbele codering je bijt:
Proxyketens. Je stuurt een verzoek naar server A, die het doorstuurt naar server B. Als beide servers de URL coderen, boem — dubbel gecodeerd. API gateways zoals Kong, AWS API Gateway of nginx reverse proxies zijn veelvoorkomende boosdoeners.
Redirectketens. Gebruiker gaat naar pagina A, wordt doorgestuurd naar pagina B met een ?returnUrl=... parameter, en pagina B stuurt opnieuw door met de return-URL als parameter. Elke redirect kan de URL opnieuw coderen. Na drie redirects is je URL driedubbel gecodeerd en compleet verminkt.
Framework-"helpers". Sommige webframeworks coderen URL-parameters automatisch. Als je handmatig codeert voordat je het aan het framework doorgeeft, krijg je dubbele codering. Ik heb dit zien gebeuren met Spring Boot, Express.js middleware en Django's URL reversing.
Hoe detecteer je dubbele codering? Zoek naar %25 in de URL. Dat is een procentteken dat gecodeerd is, wat meestal betekent dat iets twee keer gecodeerd is. Als je %2520 ziet, is dat een dubbel gecodeerde spatie. Als je %253D ziet, is dat een dubbel gecodeerd = teken.
Hoe los je het op:
De beste verdediging is duidelijke grenzen in je code vast te stellen: codeer aan de randen (vlak voor het verzenden van een HTTP-verzoek, of vlak voor het samenstellen van een URL om weer te geven), en geef intern overal ruwe, ongecodeerde strings door.
URL-lengtelimieten en wat eraan te doen
Hier is iets dat je zal verrassen: de HTTP-specificatie zelf definieert GEEN maximale URL-lengte. RFC 3986 zegt dat URLs "onbeperkt in lengte" moeten zijn. Maar de echte wereld is het daar niet mee eens.
Verschillende componenten in de keten hebben hun eigen limieten, en de kortste wint:
| Component | Maximale URL-lengte |
| Internet Explorer (RIP) | 2.083 tekens |
| Chrome, Firefox, Safari | ~65.000+ tekens |
| Apache (standaard) | 8.190 tekens |
| Nginx (standaard) | 8.192 tekens |
| IIS (standaard) | 16.384 tekens |
| AWS ALB | 8.192 tekens |
| Cloudflare | 32.768 tekens |
Die oude 2.083-tekenlimiet van IE bepaalde vroeger alles. Hoewel IE nu eigenlijk dood is, behandelen sommige ontwikkelaars en tools het nog steeds als evangelie. Maar in de praktijk kunnen de meeste moderne stacks veel langere URLs aan.
Dat gezegd hebbende, alleen omdat je een URL van 65.000 tekens KAN maken, betekent niet dat je dat MOET doen. Hier zijn echte redenen om URLs kort te houden:
- Serverlogs. Veel logsystemen knippen lange URLs af, wat debuggen een nachtmerrie maakt.
- Kopiëren en plakken. Gebruikers kopiëren en delen URLs. Superlange URLs breken in e-mails, chatberichten en documenten.
- SEO. Zoekmachines raden over het algemeen aan URLs onder de 2.000 tekens te houden.
- Caching. Sommige CDN- en proxy-cachesleutellimieten zijn URL-gebaseerd. Langere URLs = meer cache misses.
Dus wat doe je als je URL te lang wordt? Fijn dat je het vraagt:
1. Gebruik POST in plaats van GET. Als je veel data verstuurt, zet het in de request body. De request body heeft geen praktische maximumgrootte. Dit is de meest voorkomende oplossing voor complexe zoekformulieren met veel filters.
2. Gebruik URL-verkorters of referentie-ID's. Genereer een kort token dat verwijst naar de volledige set parameters die server-side opgeslagen zijn. Dus in plaats van /search?filter1=abc&filter2=def&filter3=... krijg je /search/saved/abc123.
3. Comprimeer je parameters. Sommige apps base64-coderen een gecomprimeerde JSON-blob en zetten die in de URL. Niet mooi, maar het werkt. Je ziet dit bij tools zoals Grafana dashboard-URLs.
Formuliercodering: application/x-www-form-urlencoded
Laten we het hebben over de olifant in de kamer die URL-codering nog verwarrender maakt: HTML-formuliercodering. Als je een HTML-formulier indient met method="POST", codeert de browser de formulierdata met een formaat genaamd application/x-www-form-urlencoded. En dit formaat is BIJNA hetzelfde als standaard procentcodering, maar met één cruciaal verschil dat iedereen gek maakt.
Spaties worden + in plaats van %20.
Dat is het. Dat is het belangrijkste verschil. Maar jeetje, wat veroorzaakt het verwarring.
Waarom bestaat dit verschil? Historische redenen, uiteraard. De HTML-formuliercoderingsspecificatie is ouder dan de URL-coderingsspecificatie, en het gebruikte + voor spaties omdat... tja, iemand vond het er mooier uitzien in de jaren '90. En nu zitten we er voor altijd mee opgescheept.
Als je een HTML-formulier indient, stuurt de browser een Content-Type: application/x-www-form-urlencoded header, en de server weet dat + als spaties geïnterpreteerd moeten worden. Maar als je handmatig URLs bouwt in JavaScript en + voor spaties in het padgedeelte gebruikt, ziet de server letterlijke plustekens. Leuke tijden.
Hier is het in de praktijk belangrijk:
En dan is er multipart/form-data, wat een compleet ander beest is. Als je formulier bestandsuploads bevat (), schakelt de browser over naar multipart/form-data codering, die boundaries gebruikt om velden te scheiden in plaats van &-tekens. Het gebruikt helemaal geen URL-codering — elk deel heeft zijn eigen headers en body. Als je ooit handmatig een multipart verzoek hebt geprobeerd te parsen, ken je de pijn.
Het praktische advies: gebruik URLSearchParams voor query strings en formulierdata. Gebruik FormData voor bestandsuploads. Probeer deze dingen niet handmatig te coderen, tenzij je het echt, echt moet.
Probeer het zelf
Werk je met URLs die er vreemd uitzien? Plak ze in onze URL-decoder om de werkelijke tekens te zien. Moet je een waarde coderen voordat je het in een URL plaatst? De URL-encoder regelt het direct. En om complexe URLs in hun componenten op te splitsen, gebruik de URL-parser om elk onderdeel duidelijk te zien.