Jeg vedder på at du har hatt en bug der en URL med mellomrom eller spesialtegn ødela noe. Kanskje et & i en søkestreng forårsaket merkelig oppførsel. Eller du så %20 i en URL og lurte på hva det var. La oss rydde opp i alt dette.

Hvorfor URL-er trenger koding

URL-er kan bare inneholde et begrenset sett med tegn. RFC 3986-spesifikasjonen definerer "ikke-reserverte" tegn som alltid er trygge: A-Z, a-z, 0-9, -, _, . og ~. Alt annet — mellomrom, &, =, ?, ikke-ASCII-tegn som ñ eller 日本語 — må prosentkodes.

Hvordan prosentkoding fungerer

Det er rett frem: ta tegnets UTF-8-byteverdi og representer hver byte som % etterfulgt av to heksadesimale sifre.

Eksempler:

  • Mellomrom → %20
  • &%26
  • =%3D
  • é%C3%A9 (to byte i UTF-8)
  • %E6%97%A5 (tre byte i UTF-8)

Hello World blir Hello%20World i en URL-sti.

Feil nummer 1: dobbel koding

Dette er den vanligste URL-kodingsfeilen jeg ser, og den er subtil. Du koder en streng, sender den til en funksjon som koder den igjen. Nå blir %20 (et allerede kodet mellomrom) til %2520 fordi %-tegnet selv blir kodet.

Eksempel på problemet:

javascript

Løsningen: kod verdier én gang, på riktig nivå. Ikke kod ting som allerede er kodet.

Plusstegn vs. %20 — Ja, det er forvirrende

I URL-spørrestrenger kan mellomrom være enten + eller %20. +-konvensjonen kommer fra HTML-skjemakoding (application/x-www-form-urlencoded). I URL-stier er bare %20 gyldig.

https://example.com/hello world → stien kodes til https://example.com/hello%20world

Men https://example.com/search?q=hello world → spørringen kan være ?q=hello+world eller ?q=hello%20world

encodeURI vs. encodeURIComponent

JavaScript gir deg to funksjoner, og å bruke feil er en klassisk feil:

  • encodeURI() — Koder en hel URL. Lar :, /, ?, #, &, = stå urørt fordi de er strukturelle deler av URL-en.
  • encodeURIComponent() — Koder en URL-komponent (som en spørreparameterverdi). Koder OGSÅ :, /, ?, #, &, = fordi de kan være del av dataene.

Tommelfingerregel: bruk encodeURIComponent() for verdier, aldri for hele URL-er. MDN-dokumentasjonen forklarer forskjellen utmerket.

Andre programmeringsspråk

  • Python: urllib.parse.quote() for stier, urllib.parse.urlencode() for spørrestrenger — dokumentasjon her
  • Java: URLEncoder.encode() (bruker + for mellomrom — pass på!)
  • PHP: urlencode() for spørrestrenger, rawurlencode() for stier

Raske tips

  • Kod alltid parameter-verdier, aldri hele URL-er
  • Dekod én gang på mottakersiden — ikke send kodede verdier gjennom flere lag
  • Test med grensetilfeller: mellomrom, &, =, #, ikke-ASCII-tegn og emoji

Bygge URL-er trygt i JavaScript

Den riktige måten å konstruere URL-er med spørreparametere er å bruke de innebygde URL- og URLSearchParams-API-ene. De håndterer all koding for deg:

javascript

Legg merke til hvordan URLSearchParams bruker + for mellomrom (HTML-skjemakodingskonvensjon) og koder & i verdien riktig slik at den ikke forveksles med & som separerer parametere. Dette er mye tryggere enn strengsammensetning.

Vanlige URL-kodingsfeller

1. Kode hele URL-en i stedet for bare verdiene. Hvis du kjører encodeURIComponent() på en hel URL, vil den kode ://, /, ? og &-tegnene — noe som gjør URL-en helt ubrukelig. Kod bare individuelle parameterverdier.

2. Glemme å kode hash-fragmenter. #-tegnet i en URL starter en fragmentidentifikator. Hvis dataene dine inneholder # og du ikke koder det, forsvinner alt etter det fra serverens perspektiv. Serveren ser aldri fragmentidentifikatorer — de er kun på klientsiden.

3. Ikke håndtere internasjonale domenenavn (IDN). Domenenavn med ikke-ASCII-tegn som example.日本 trenger spesiell håndtering kalt Punycode-koding. Dette er separat fra prosentkoding og gjelder kun vertsnavndelen av URL-en.

4. Kode mellomrom inkonsistent. Noen deler av systemet ditt kan kode mellomrom som + og andre som %20. Dette fungerer vanligvis bra for dekoding, men kan forårsake problemer med URL-sammenligning og caching. Velg én konvensjon og hold deg til den.

Prosentkoding hurtigreferanse

TegnKodetHvorfor det trenger koding
Mellomrom%20 eller +Ikke tillatt i URL-er
&%26Separerer spørreparametere
=%3DSeparerer nøkkel fra verdi
?%3FStarter spørrestreng
#%23Starter fragmentidentifikator
%%25Selve escape-tegnet
/%2FStiseparator
@%40Brukes i userinfo-seksjonen

URL-koding i forskjellige kontekster

URL-koding er ikke bare for nettleser-URL-er. Du vil støte på det i disse vanlige situasjonene:

  • API-forespørsler: Spørreparametere i REST API-kall trenger riktig koding, spesielt hvis de inneholder brukerinndata
  • Omdirigerings-URL-er: Når du sender en retur-URL som parameter (som ?redirect=https://...), må hele omdirigerings-URL-en kodes som en verdi
  • OAuth-flyter: OAuth-callback-URL-er og state-parametere er notorisk vanskelige fordi de involverer flere lag av URL-koding
  • Deep links: Mobile deep links følger de samme URL-kodingsreglene, men noen plattformer har tilleggskrav

Feilsøking av URL-kodingsproblemer

Når noe går galt med URL-koding, her er min feilsøkingssjekkliste:

1. Sjekk for dobbel koding — Se etter %25 i URL-en, noe som betyr at en % ble kodet to ganger

2. Sjekk den rå forespørselen — Bruk nettleserens DevTools Network-fane for å se nøyaktig hvilken URL som ble sendt, før nettleseren viser den dekodede versjonen

3. Sammenlign kodet vs. dekodet — Lim inn den problematiske URL-en i en dekoder for å se hva den faktisk inneholder

4. Sjekk serversidedekoding — Noen rammeverk dekoder URL-parametere automatisk, og å gjøre det manuelt oppå det forårsaker problemer

Anatomien til en URL

OK, la oss ta et skritt tilbake. Før vi dykker dypere inn i koding, trenger du faktisk å forstå hva en URL er laget av. Jeg vet, jeg vet — du har brukt URL-er hele karrieren din. Men jeg vedder på at du ikke kjenner alle delene ved deres offisielle navn. De fleste utviklere gjør det ikke, og det er der kodingsforvirringen starter.

I følge RFC 3986 har en URL denne strukturen:

plaintext

La meg gå gjennom hver del:

  • Scheme (https, ftp, mailto) — Protokollen. Ingen koding nødvendig her, det er alltid ASCII-bokstaver.
  • Authority — Dette inkluderer den valgfrie user:password@ (som du i praksis aldri bør bruke i 2026), verten (domenenavn eller IP), og et valgfritt portnummer.
  • Path (/search/results) — Den hierarkiske delen. Skråstreker / separerer segmenter. Innenfor hvert segment må du kode spesialtegn, men du koder IKKE selve skråstrekene.
  • Query (?q=hello&lang=en) — Nøkkel-verdi-par etter ?. & separerer par, = separerer nøkler fra verdier. Du koder nøklene og verdiene, men ikke de strukturelle & og =.
  • Fragment (#section-2) — Delen etter #. Denne er interessant — den sendes aldri til serveren. Den er rent klientsiden. Men du må fortsatt kode spesialtegn inni den.

Her er det som forvirrer folk: forskjellige deler av URL-en har forskjellige kodingsregler. En / er helt fin i stien (den er en separator!) men trenger å kodes som %2F hvis den dukker opp inne i en spørreparameterverdi. Et @-tegn er greit i authority-seksjonen men bør kodes i stien. Dette er grunnen til at universelle kodingsfunksjoner forårsaker så mange feil.

Tenk på det slik: URL-en er en setning med grammatikkregler. Spesialtegnene er tegnsetting. Du ville ikke kodet et komma som faktisk brukes som et komma — du koder bare et komma som er del av dataene og kan forveksles med tegnsetting.

encodeURI vs. encodeURIComponent: JavaScripts minefelt

Greit, dette temaet gjør meg helt gal fordi jeg ser utviklere gjøre det feil HELE TIDEN. JavaScript gir deg to kodingsfunksjoner, og de høres nesten identiske ut, men å bruke feil vil ødelegge dagen din.

La meg legge det frem tydelig:

encodeURI() er designet for å kode en hel URL. Den koder usikre tegn men lar de strukturelle være — ting som :, /, ?, #, &, =, @. Fordi hvis du koder en hel URL, vil du åpenbart ikke ødelegge URL-strukturen.

encodeURIComponent() er designet for å kode en enkelt verdi som går inn i en URL. Den koder ALT unntatt bokstaver, sifre og - _ . ~. Dette inkluderer :, /, ?, #, &, = — fordi når disse dukker opp i en verdi, er de data, ikke struktur.

Her er en sammenligning som gjør det krystallklart:

TegnencodeURI()encodeURIComponent()
:: (uendret)%3A
// (uendret)%2F
?? (uendret)%3F
## (uendret)%23
&& (uendret)%26
== (uendret)%3D
@@ (uendret)%40
Mellomrom%20%20
é%C3%A9%C3%A9

Nå blir det skummelt. Se hva som skjer når du bruker feil:

javascript

Og den motsatte feilen er like ille:

javascript

Den gylne regelen: encodeURIComponent for verdier, encodeURI for hele URL-er. Eller enda bedre, bare bruk URL API-et og la nettleseren håndtere det. Seriøst, URL- og URLSearchParams-klassene finnes av en grunn. Se MDN encodeURIComponent-dokumentasjonen og MDN encodeURI-dokumentasjonen for alle detaljene.

URL-koding i forskjellige programmeringsspråk

JavaScript er ikke det eneste språket med forvirrende URL-kodingsfunksjoner. Hvert språk har sine egne særheter, og jeg tuller ikke, noen av dem er enda mer forvirrende enn JavaScripts par.

Python — Faktisk ganske fornuftig, når du finner riktig modul:

python

Java — Her blir det rart. URLEncoder ble designet for HTML-skjemakoding, ikke generell URL-koding. Så mellomrom blir + i stedet for %20:

java

C# — .NET gir deg faktisk de riktige verktøyene, men det finnes omtrent fem forskjellige metoder og de gjør alle litt forskjellige ting:

csharp

PHP — Å, PHP. Selvfølgelig har du to funksjoner med nesten identiske navn som gjør litt forskjellige ting:

php

Go — Rent og fornuftig, som Go pleier å være:

go

Konklusjonen? Hvert språk håndterer + vs. %20-temaet forskjellig, og hvert språk har minst én funksjon som vil overraske deg. Sjekk alltid dokumentasjonen for språket du jobber med. Ikke anta at det fungerer likt som i JavaScript.

Unicode i URL-er: Det ville vesten

OK, her blir det VIRKELIG interessant. Hva skjer når du putter ikke-ASCII-tegn i en URL? Altså, hva om du vil ha en URL med japansk, arabisk eller — jeg tuller ikke — emoji?

Svaret involverer to helt forskjellige systemer, og å blande dem er en klassisk feil.

For sti- og spørredelene: Du bruker prosentkoding. Ta tegnets UTF-8-bytes og kod hver enkelt. Så café blir caf%C3%A9. Nettleseren din gjør dette automatisk og viser deg vanligvis den pene versjonen i adressefeltet, men under panseret sender den den prosentkodede versjonen.

For domenenavn: Prosentkoding brukes IKKE. I stedet finnes det et vilt system kalt Punycode. Det konverterer Unicode-domenenavn til ASCII-kompatible strenger som starter med xn--.

Sjekk dette:

  • café.comxn--caf-dma.com
  • münchen.dexn--mnchen-3ya.de
  • 例え.jpxn--r8jz45g.jp

Hvorfor to forskjellige systemer? Fordi DNS (systemet som gjør domenenavn om til IP-adresser) ble bygget på 1980-tallet og støtter kun ASCII. Så de måtte finne opp en måte å stappe Unicode inn i ASCII-strenger — og Punycode er den oppfinnelsen. Sti- og spørredelene av en URL, derimot, håndteres av webservere som kan håndtere prosentkodede bytes.

Det finnes faktisk en hel spesifikasjon for Unicode i URL-er kalt IRI (Internationalized Resource Identifiers) definert i RFC 3987. En IRI er i bunn og grunn en URL som tillater Unicode-tegn direkte. Nettlesere konverterer IRI-er til URI-er bak kulissene.

Og ja, emoji-domener finnes. 💩.la er et ekte domene (eller var det på et tidspunkt). Det Punycode-kodes til xn--ls8h.la. Jeg anbefaler ikke å bruke emoji-domener til noe seriøst, men det er et morsomt bevis på at systemet fungerer.

En fallgruve med Unicode i URL-er: forskjellige Unicode-representasjoner av det "samme" tegnet. For eksempel kan é representeres som ett enkelt kodepunkt (U+00E9) eller som e + et kombinerende aksenttegn (U+0065 + U+0301). Disse produserer forskjellige prosentkodede strenger! WHATWG URL-standarden anbefaler NFC-normalisering, men ikke alle systemer følger dette konsekvent.

Dobbel koding: Feilen som hjemsøker drømmene dine

Jeg nevnte dobbel koding tidligere, men det fortjener sitt eget dypdykk fordi denne feilen sannsynligvis har kastet bort flere kollektive utviklertimer enn noe annet URL-relatert problem. Vært der, gjort det — flere ganger.

Her er det grunnleggende scenariet:

plaintext

Hva skjedde? Når du koder hello%20world en gang til, blir %-tegnet kodet til %25. Så %20 blir %2520. Serveren ser den bokstavelige strengen %20 i stedet for et mellomrom.

Dette høres opplagt ut når jeg skriver det slik, men i ekte kodebaser er det utrolig snikende. Her er scenariene der dobbel koding biter deg:

Proxy-kjeder. Du sender en forespørsel til server A, som videresender den til server B. Hvis begge servere koder URL-en, bam — dobbelt kodet. API-gatewayer som Kong, AWS API Gateway eller nginx reverse proxyer er vanlige syndere.

Omdirigeringskjeder. Bruker går til side A, blir omdirigert til side B med en ?returnUrl=...-parameter, og side B omdirigerer igjen med retur-URL-en som parameter. Hver omdirigering kan re-kode URL-en. Etter tre omdirigeringer er URL-en din trippelkodet og helt ødelagt.

Rammeverk-"hjelpere". Noen web-rammeverk koder URL-parametere automatisk. Hvis du manuelt koder før du sender til rammeverket, får du dobbel koding. Jeg har sett dette skje med Spring Boot, Express.js-mellomvare og Djangos URL-reversering.

Hvordan oppdager du dobbel koding? Se etter %25 i URL-en. Det er et prosenttegn som har blitt kodet, noe som vanligvis betyr at noe ble kodet to ganger. Hvis du ser %2520, er det et dobbeltkodet mellomrom. Hvis du ser %253D, er det et dobbeltkodet =-tegn.

Slik fikser du det:

javascript

Det beste forsvaret er å etablere klare grenser i koden din: kod på kantene (rett før du sender en HTTP-forespørsel, eller rett før du konstruerer en URL for visning), og send rå, ukodede strenger overalt ellers internt.

URL-lengdebegrensninger og hva du gjør med dem

Her er noe som vil overraske deg: HTTP-spesifikasjonen selv definerer IKKE en maksimal URL-lengde. RFC 3986 sier at URL-er bør ha "ubegrenset lengde". Men den virkelige verden er uenig.

Forskjellige komponenter i kjeden har sine egne begrensninger, og den korteste vinner:

KomponentMaks URL-lengde
Internet Explorer (RIP)2 083 tegn
Chrome, Firefox, Safari~65 000+ tegn
Apache (standard)8 190 tegn
Nginx (standard)8 192 tegn
IIS (standard)16 384 tegn
AWS ALB8 192 tegn
Cloudflare32 768 tegn

Den gamle 2 083-tegnsbegrensningen fra IE styrte alt. Selv om IE i praksis er død nå, behandler noen utviklere og verktøy den fortsatt som evangelium. Men i praksis kan de fleste moderne stacker håndtere mye lengre URL-er.

Likevel, bare fordi du KAN lage en 65 000 tegn lang URL betyr ikke at du BØR. Her er noen reelle grunner til å holde URL-er korte:

  • Serverlogger. Mange loggingssystemer kutter lange URL-er, noe som gjør feilsøking til et mareritt.
  • Kopier og lim inn. Brukere kopierer og deler URL-er. Superlange URL-er brekker i e-poster, chatmeldinger og dokumenter.
  • SEO. Søkemotorer anbefaler generelt å holde URL-er under 2 000 tegn.
  • Caching. Noen CDN- og proxy-cachenøkkelbegrensninger er URL-baserte. Lengre URL-er = flere cache-bommerter.

Så hva gjør du når URL-en din blir for lang? Glad du spør:

1. Bruk POST i stedet for GET. Hvis du sender mye data, legg det i forespørselskroppen. Forespørselskroppen har ingen praktisk størrelsesbegrensning. Dette er den vanligste løsningen for komplekse søkeskjemaer med mange filtre.

2. Bruk URL-forkortere eller referanse-ID-er. Generer et kort token som kartlegger til det fulle parametersettet lagret på serversiden. Så i stedet for /search?filter1=abc&filter2=def&filter3=... får du /search/saved/abc123.

3. Komprimer parameterne dine. Noen apper base64-koder en komprimert JSON-blob og putter den i URL-en. Ikke pent, men det fungerer. Du ser dette i verktøy som Grafana-dashboard-URL-er.

Skjemakoding: application/x-www-form-urlencoded

La oss snakke om elefanten i rommet som gjør URL-koding enda mer forvirrende: HTML-skjemakoding. Når du sender et HTML-skjema med method="POST", koder nettleseren skjemadataene med et format kalt application/x-www-form-urlencoded. Og dette formatet er NESTEN det samme som standard prosentkoding, men med én nøkkelforskjell som driver alle gale.

Mellomrom blir + i stedet for %20.

Det er det. Det er hovedforskjellen. Men, herregud, den forårsaker forvirring.

plaintext

Hvorfor finnes denne forskjellen? Historiske grunner, selvfølgelig. HTML-skjemakodingsspesifikasjonen er eldre enn URL-kodingsspesifikasjonen, og den brukte + for mellomrom fordi... vel, noen syntes det så penere ut på 90-tallet. Og nå sitter vi fast med det for alltid.

Når du sender et HTML-skjema, sender nettleseren en Content-Type: application/x-www-form-urlencoded-header, og serveren vet å tolke + som mellomrom. Men hvis du bygger URL-er manuelt i JavaScript og bruker + for mellomrom i stidelen, vil serveren se bokstavelige plusstegn. Morsomme tider.

Her er det viktig i praksis:

javascript

Og så er det multipart/form-data, som er et helt annet beist. Når skjemaet ditt inkluderer filopplasting (), bytter nettleseren til multipart/form-data-koding, som bruker grenser for å separere felt i stedet for &-tegn. Den bruker ikke URL-koding i det hele tatt — hver del har sine egne headere og kropp. Hvis du noen gang har prøvd å manuelt parse en multipart-forespørsel, kjenner du smerten.

Det praktiske rådet: bruk URLSearchParams for spørrestrenger og skjemadata. Bruk FormData for filopplasting. Ikke prøv å kode disse tingene for hånd med mindre du virkelig, virkelig må.

Prøv det selv

Jobber du med URL-er som ser feil ut? Lim dem inn i vår URL-dekoder for å se de faktiske tegnene. Trenger du å kode en verdi før du putter den i en URL? URL-koderen håndterer det umiddelbart. Og for å bryte ned komplekse URL-er i komponentene deres, bruk URL-parseren for å se hver del tydelig.