Cos'è il Percent Encoding? (RFC 3986 spiegato in modo semplice)
Il percent encoding è un meccanismo definito nell'RFC 3986 per codificare i caratteri speciali negli URI sostituendoli con un segno di percentuale (%) seguito da due cifre esadecimali. Garantisce che gli URL contengano solo caratteri ASCII validi, trasmissibili in modo sicuro su internet.
Cos'è il Percent Encoding?
Il percent encoding, chiamato anche URL encoding, è il modo standard per rappresentare, all'interno di un URI (Uniform Resource Identifier), i caratteri che non sono consentiti o che hanno un significato speciale. Funziona sostituendo ogni carattere che necessita di codifica con un segno di percentuale (%) seguito da due cifre esadecimali che rappresentano il valore in byte del carattere.
Ad esempio, il carattere spazio (valore in byte 0x20) viene codificato come %20. La chiocciola @ (valore in byte 0x40) viene codificata come %40. Questo meccanismo assicura che qualsiasi dato possa essere inserito in modo sicuro in un URI senza entrare in conflitto con la sintassi dell'URI stesso.
Il termine "percent encoding" deriva dall'uso del carattere percentuale come prefisso di escape. La specifica formale è definita nell'RFC 3986, pubblicato dall'Internet Engineering Task Force (IETF) nel gennaio 2005 e redatto da Tim Berners-Lee, Roy Fielding e Larry Masinter.
Come funziona il Percent Encoding
Il processo di codifica segue questi passaggi: per prima cosa, il carattere viene convertito nella sua rappresentazione in byte utilizzando una codifica di caratteri (quasi sempre UTF-8 nel web moderno). Poi, ogni byte viene rappresentato come un segno di percentuale seguito da due cifre esadecimali (per convenzione si usano le lettere maiuscole A-F, anche se i decoder dovrebbero accettare anche quelle minuscole).
// Codifica passo-passo di un carattere spazio
// Carattere: ' ' (spazio)
// Valore in byte ASCII/UTF-8: 0x20 (32 in decimale)
// Percent-encoded: %20
// Codifica passo-passo di un carattere multibyte: e con accento acuto
// Carattere: e con accento acuto (U+00E9)
// Byte UTF-8: 0xC3 0xA9
// Percent-encoded: %C3%A9
// Codifica passo-passo di un carattere a 3 byte
// Carattere: ideogramma CJK (U+4E2D)
// Byte UTF-8: 0xE4 0xB8 0xAD
// Percent-encoded: %E4%B8%AD
Le cifre esadecimali devono sempre presentarsi in coppia. Un segno di percentuale isolato, o un segno di percentuale seguito da caratteri non esadecimali, non è valido e provoca un errore di decodifica. Questa è una causa comune dell'errore "URI malformed" in JavaScript.
Il percent encoding è sempre reversibile. Il decoder legge il segno di percentuale, interpreta i due caratteri successivi come un valore in byte esadecimale e lo riconverte nel carattere originale. Per i caratteri UTF-8 multibyte, i byte codificati consecutivi vengono combinati e decodificati insieme.
Quali caratteri devono essere codificati?
Non tutti i caratteri necessitano di percent encoding. L'RFC 3986 definisce due categorie: i caratteri non riservati (unreserved), che non necessitano mai di codifica, e i caratteri riservati (reserved), che devono essere codificati quando non vengono utilizzati per il loro scopo riservato. Tutti gli altri caratteri (inclusi spazi, caratteri non ASCII e caratteri di controllo) devono sempre essere codificati.
Caratteri che NON necessitano di codifica (unreserved): lettere maiuscole (A-Z), lettere minuscole (a-z), cifre (0-9), trattino (-), punto (.), trattino basso (_) e tilde (~). Questi 66 caratteri possono comparire in qualsiasi punto di un URI senza codifica.
Caratteri che a volte necessitano di codifica (reserved): : / ? # [ ] @ ! $ & ' ( ) * + , ; =. Questi caratteri hanno un significato sintattico speciale negli URI. Necessitano di codifica solo quando vengono usati come dati anziché come delimitatori.
Caratteri che necessitano sempre di codifica: spazi, caratteri non ASCII (qualsiasi carattere superiore a 127), caratteri di controllo (0-31 e 127) e caratteri non sicuri come < > { } | \ ^ ` ".
Caratteri riservati e non riservati nell'RFC 3986
La distinzione tra caratteri riservati e non riservati è fondamentale per il funzionamento degli URI. I caratteri riservati fungono da delimitatori che definiscono la struttura di un URI. Ad esempio, :// separa lo schema dall'authority, / separa i segmenti del percorso, ? inizia la query string e # inizia il fragment.
| Categoria | Caratteri | Quando codificare |
|---|---|---|
| Non riservati | A-Z a-z 0-9 - . _ ~ |
Mai |
| Delimitatori generali | : / ? # [ ] @ |
Quando usati come dati, non come delimitatori |
| Sotto-delimitatori | ! $ & ' ( ) * + , ; = |
Quando usati come dati in componenti che attribuiscono loro un significato |
| Tutti gli altri caratteri | Spazi, non ASCII, caratteri di controllo, ecc. | Sempre |
Un principio essenziale dell'RFC 3986 è che gli URI che differiscono solo per il fatto che un carattere riservato sia percent-encoded oppure compaia letteralmente non sono equivalenti. Ad esempio, /path/to e /path%2Fto sono URI differenti, anche se %2F decodifica in /. Il primo ha due segmenti di percorso; il secondo ha un solo segmento di percorso che contiene uno slash letterale.
Percent Encoding e URL Encoding: sono la stessa cosa?
"Percent encoding" e "URL encoding" vengono spesso usati come sinonimi e, nella maggior parte dei contesti, indicano la stessa cosa. Esiste però una sottile distinzione di natura storica. "URL encoding" può talvolta riferirsi al più vecchio formato application/x-www-form-urlencoded utilizzato dai form HTML, che codifica gli spazi come + anziché come %20.
Il formato application/x-www-form-urlencoded è stato definito nella specifica HTML e precede l'RFC 3986. Ha regole leggermente diverse: gli spazi diventano + e l'insieme dei caratteri che non vengono codificati è leggermente diverso. L'uso moderno del termine "URL encoding" si riferisce quasi sempre al percent encoding dell'RFC 3986, in cui gli spazi sono %20.
Nella pratica, dovresti usare il percent encoding dell'RFC 3986 per tutti i contesti relativi agli URI (segmenti del percorso, parametri di query nelle API REST, identificatori di fragment). Usa il formato application/x-www-form-urlencoded solo quando hai a che fare con l'invio di form HTML o quando un'API specifica lo richiede.
// Percent encoding RFC 3986 (consigliato per gli URI)
encodeURIComponent('hello world') // "hello%20world"
// application/x-www-form-urlencoded (form HTML)
new URLSearchParams({q: 'hello world'}).toString() // "q=hello+world"
// Equivalente in Python
from urllib.parse import quote, quote_plus
quote('hello world', safe='') # "hello%20world"
quote_plus('hello world') # "hello+world"