8 min di letturaIngegneria

URL encoding spiegato: quali caratteri codificare

L'URL encoding sostituisce un carattere con un simbolo di percentuale e due cifre esadecimali così che non venga letto come sintassi URL. Quali caratteri lo richiedono, e dove si rompe.

Marius Voß
DevRel · edge infra
URL encoding mostrato come una query string in cui uno spazio e una e commerciale diventano sequenze percentuali dentro il valore di un parametro

L'URL encoding sostituisce un carattere con un simbolo di percentuale e due cifre esadecimali: uno spazio diventa %20, una e commerciale diventa %26, un punto interrogativo diventa %3F. Lo scopo è impedire che un carattere venga letto come sintassi URL quando invece intendevi usarlo come dato. Niente di più.

Il motivo per cui sembra più complicato di così è che quasi ogni domanda sull'argomento è in realtà una domanda sulla portata. Quali caratteri, in quale parte dell'URL, codificati da quale livello? Sbaglia la portata e ottieni uno di due fallimenti classici: un parametro di tracciamento che si tronca silenziosamente, oppure una destinazione che arriva come https%3A%2F%2Fexample.com e restituisce 404. Questo articolo copre i due insiemi di caratteri che decidono la risposta, i punti in cui le regole cambiano, e come controllare cosa porta davvero un link. Per il quadro più ampio di cosa fa un redirect con tutto questo, vedi tipi di redirect.

I due insiemi che decidono tutto

La sezione 2.3 di RFC 3986 definisce un insieme unreserved che non ha mai bisogno di codifica: lettere, cifre, ed esattamente quattro segni di punteggiatura - trattino, punto, underscore, tilde. Se il tuo valore contiene solo questi, non devi fare nulla.

Tutto il resto cade in una di due categorie. I caratteri reserved portano un significato strutturale: : / ? # [ ] @ separano le parti di un URL, e ! $ & ' ( ) * + , ; = separano le cose dentro quelle parti. La sezione 2.2 li elenca. Sono legali come sintassi e devono essere codificati quando compaiono come dati. Il resto è tutto ciò che sta fuori dall'ASCII, che viene codificato byte per byte dopo essere stato convertito in UTF-8 - il motivo per cui una lettera accentata costa di solito sei caratteri invece di tre.

Questo dà l'unica regola che vale la pena memorizzare: codifica un carattere quando è un dato e altrimenti verrebbe letto come sintassi. Una e commerciale tra due parametri è sintassi. Una e commerciale dentro il nome di una campagna è un dato, e se la lasci intatta la lista dei parametri finisce lì.

Una query string in cui il valore della campagna contiene uno spazio e una e commerciale, mostrata codificata correttamente dentro il valore e in modo scorretto sull'intero URL

Codifica il valore, non l'URL

È l'errore che vedo più spesso, e ha sempre la stessa forma. Qualcuno ha un URL, sa che ha bisogno di essere codificato, quindi incolla l'intera stringa in un encoder e ottiene:

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

Quella stringa non è un URL. È un pezzo di testo a forma di URL che può essere soltanto un valore dentro un altro URL - esattamente il posto in cui appartiene quando stai passando una destinazione attraverso un redirector, ed esattamente il posto in cui non appartiene quando stai cercando di aprirla.

Il trattamento corretto codifica ogni valore per conto proprio:

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

Schema, host, separatori di percorso e i simboli ? e & restano come sintassi. È cambiato solo il valore. Ogni linguaggio distribuisce due funzioni per questa distinzione, e scegliere quella sbagliata è l'altra metà del problema: la pagina di MDN su encodeURIComponent è netta sul fatto che encodeURI lascia deliberatamente intatti i caratteri reserved perché si aspetta un URI intero, mentre encodeURIComponent li escapa perché si aspetta un frammento di uno. I valori vogliono encodeURIComponent. In Python è urllib.parse.quote, in Go url.QueryEscape, in PHP rawurlencode.

Lo spazio è %20, tranne dove è un più

Entrambi sono corretti, in posti diversi, ed è la cosa più confusa in assoluto su questo argomento.

In un percorso o in un URI generico, uno spazio è %20. In una query string costruita come la costruisce un form HTML, uno spazio è +, perché è ciò che specifica la serializzazione application/x-www-form-urlencoded nello standard URL del WHATWG. Entrambe le forme vengono lette come uno spazio da ogni parser di query lato server che probabilmente incontrerai.

La trappola è la direzione opposta. Se un segno più è un dato - un numero di telefono, un termine di ricerca, una campagna chiamata spring+summer - deve essere scritto %2B. Lasciato intatto in una query string diventa uno spazio, e passerai un pomeriggio a chiederti perché il numero nel tuo CRM ha perso il prefisso internazionale.

CarattereCodificatoPerché conta
spazio%20 o ++ solo dentro una query string, %20 ovunque
&%26Non codificato, la lista dei parametri finisce lì
?%3FNon codificato, tutto ciò che segue diventa la query
#%23Non codificato, il resto non arriva mai al server
+%2BNon codificato in una query, arriva come uno spazio
%%25Non codificato, inghiottisce i due caratteri successivi

La riga # merita una nota, perché è quella che produce la segnalazione di bug più confusa. Un fragment non viene mai inviato al server. Metti un # non codificato in una destinazione di redirect e il server vede un URL troncato mentre la barra degli indirizzi del browser continua a sembrare corretta, quindi chi lo segnala giura che il link funziona.

Se costruisci a mano gli URL di campagna più che occasionalmente, fermati: il nostro UTM builder codifica ogni valore mentre lo scrivi, e le convenzioni di denominazione UTM copre come scegliere valori che non hanno bisogno di codifica fin dall'inizio. Accorcia il risultato sul tuo dominio e il pasticcio codificato smette di essere qualcosa che qualcuno deve guardare.

Il double encoding, e come riconoscerlo

Il double encoding è ciò che succede quando un valore passa attraverso due livelli che fanno entrambi il proprio lavoro. Il simbolo di percentuale è a sua volta un carattere che deve essere escapato, quindi %20 diventa %2520, e %2520 diventa %252520.

I sintomi sono riconoscibili una volta che li hai visti. Un titolo di pagina che mostra spring%20sale a un visitatore reale. Un parametro che arriva nell'analytics con sequenze di escape visibili. Un redirect che funziona al primo salto e fallisce al secondo. La causa è quasi sempre una chiamata di codifica applicata a un valore che era arrivato già codificato, spesso perché proveniva da un database che memorizzava la forma codificata.

La correzione è decidere quale livello possiede la codifica e rendere gli altri non invasivi. Decodifica una volta quando leggi un valore, codifica una volta quando lo scrivi in un URL, e non fare mai entrambe le cose nella stessa funzione.

Un valore che passa attraverso due livelli di codifica così che uno spazio diventi %20 e poi %2520, con il sintomo visibile nel browser

Dove questo morde nella pratica

Tre posti, nell'ordine in cui probabilmente li incontrerai.

Parametri di tracciamento. Un valore di campagna con una e commerciale non codificata tronca la lista dei parametri, quindi la sessione finisce nel tuo analytics come traffico diretto e la campagna non riceve alcun merito. Nulla genera errori. Parametri UTM non visualizzati in GA4 copre la diagnosi dal lato del reporting, e i browser eliminano i parametri UTM copre l'altro motivo per cui un parametro può sparire tra il clic e la pagina.

Redirect. Le regole del server ricodificano in modo incoerente, e se una query string sopravvive del tutto dipende dalla direttiva che hai usato. Un redirect 301 in .htaccess ha la tabella completa per Apache; la versione breve è che una regola che sostituisce la query string eliminerà silenziosamente la tua.

QR code. La codifica gonfia la lunghezza del payload, e la lunghezza del payload decide quanto denso sia il codice stampato. Ogni spazio costa tre caratteri invece di uno, ogni lettera accentata sei. Un URL di tracciamento con un paio di nomi di campagna codificati può far salire un codice di una versione o due, il che è una differenza reale alla dimensione di un biglietto da visita - il QR code che non si scansiona inserisce la lunghezza del payload tra le quattro cause esattamente per questo motivo. Codificare un link breve invece dell'URL completo è la correzione più economica disponibile.

Due comandi risolvono quasi ogni discussione. Il primo mostra cosa riceve il server dopo un redirect:

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

Il secondo costruisce la codifica al posto tuo invece di fidarsi delle tue dita, il che è utile quando un valore contiene diversi caratteri problematici in una volta:

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

Leggi l'output come dato, non come decorazione. Se vedi %2520 hai un problema di double encoding, se vedi un valore che finisce troppo presto hai un separatore non codificato, e se vedi %3A%2F%2F all'inizio hai codificato l'intero URL. Il nostro link checker fa la metà relativa al redirect in un browser se preferisci non aprire un terminale.

L'abitudine che vale la pena costruire è guardare l'URL finale una volta, a occhio, prima che una campagna venga lanciata. I bug di codifica sono invisibili in un browser e evidenti in un terminale, e ti costano l'attribuzione piuttosto che l'uptime, il che è il motivo per cui sopravvivono così a lungo.

Leggi la serie cornerstone

Questo articolo fa parte del cluster engineering. Per il lato dei redirect, tipi di redirect copre ogni codice di stato e metodo lato client, e come funzionano gli URL shortener copre cosa succede tra il clic e la pagina.

Correlati nel blog

Domande frequenti

Cos'è l'URL encoding?

Sostituire un carattere con un simbolo di percentuale seguito dal suo valore byte in esadecimale, così che il carattere non possa essere confuso con la sintassi URL. Uno spazio diventa %20, una e commerciale diventa %26, un punto interrogativo diventa %3F. Il meccanismo è definito in RFC 3986 ed è anche chiamato percent-encoding.

Quali caratteri devono essere codificati per URL?

Tutto ciò che sta fuori dal set unreserved, che RFC 3986 definisce come lettere, cifre e i quattro caratteri trattino, punto, underscore e tilde. Tutto il resto è o punteggiatura reserved che porta un significato strutturale, o un byte fuori dall'ASCII, ed entrambi devono essere percent-encoded quando compaiono dentro un valore invece che come sintassi.

Devo codificare l'intero URL o solo alcune parti?

Solo le parti. Far passare un URL completo attraverso un encoder trasforma https://example.com in https%3A%2F%2Fexample.com, che non è più un URL. Codifica ogni valore di parametro della query e ogni segmento di percorso separatamente, e lascia intatti lo schema, l'host e i separatori.

Uno spazio è %20 o un segno più?

Entrambi, in posti diversi. In un percorso e in un URI generico, uno spazio è %20. In una query string costruita come la costruisce un form HTML, uno spazio è un segno più, perché è ciò che specifica la serializzazione application/x-www-form-urlencoded. Un più letterale dentro un valore di query deve quindi essere scritto %2B, altrimenti verrà letto come uno spazio.

Cos'è il double encoding?

Codificare qualcosa che era già stato codificato, così %20 diventa %2520 perché il simbolo di percentuale stesso viene escapato in %25. Il sintomo è una pagina che mostra un %20 letterale nel suo testo, o un parametro che arriva con sequenze di escape visibili. È quasi sempre un valore passato attraverso due livelli che lo hanno entrambi, utilmente, codificato.

Perché i caratteri codificati rendono un QR code più difficile da scansionare?

Perché ognuno costa tre caratteri invece di uno. Uno spazio è un carattere di intento e tre di payload, quindi una manciata di essi può far salire il codice di una versione o due, il che significa più moduli nella stessa area stampata. Codificare un lungo URL di tracciamento in un QR è uno dei modi più rapidi per ottenere un codice che si scansiona solo a distanza ridotta.

Prova Elido

Incolla un URL, ottieni un link breve

Senza registrazione. Il link vive 30 giorni. Iscriviti per conservarlo.

Gratis, nessuna registrazione richiesta · 2 al giorno

Prova Elido

Accorciatore di URL ospitato nell'UE: domini personalizzati, analisi approfondite e API aperta. Piano gratuito - senza carta di credito.

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

Continua a leggere