Comparison
Base64 vs base64url: due caratteri che fanno la differenza
Stesso alfabeto, ultimi due caratteri diversi, padding diverso. Una riga di differenza, casi d’uso completamente diversi.
By Buğra SözeriPublished
In sintesi. Base64 e base64url sono la stessa codifica tranne per tre dettagli definiti in RFC 4648: + diventa -, / diventa _, e il padding = viene eliminato. Usa base64url ovunque il valore codificato viaggi in un URL, JWT o cookie firmato; usa Base64 standard per gli allegati email, HTTP Basic Auth e data URI.
Entrambi sono definiti in RFC 4648. Il Base64standard (§4) e il base64url(§5) differiscono in esattamente tre dettagli: due sostituzioni nell’alfabeto e il trattamento del padding. Tutto qui. Ma confonderli rompe i JWT, i cookie firmati e tutto ciò che viaggia in un URL.
La differenza
| Proprietà | Base64 standard | Base64url |
|---|---|---|
| 62° carattere | + | - |
| 63° carattere | / | _ |
| Padding | = fino a multiplo di 4 | Di norma omesso |
| Sicuro per URL? | No (+, /, = richiedono percent-encoding) | Sì |
| Definito in | RFC 4648 §4 | RFC 4648 §5 |
Lo stesso input, entrambe le codifiche
Codifica di https://example.com/?q=hello world:
- Standard:
aHR0cHM6Ly9leGFtcGxlLmNvbS8/cT1oZWxsbyB3b3JsZA== - Base64url:
aHR0cHM6Ly9leGFtcGxlLmNvbS8_cT1oZWxsbyB3b3JsZA
Si noti che la / nella forma standard diventa_ in base64url, e il == finale viene eliminato.
Perché gli URL hanno bisogno della propria variante
L’alfabeto Base64 standard include + e/. Entrambi sono riservati nella sintassi degli URL:
/è il separatore di percorso.+è, per convenzione nelle query string codificate come form, interpretato come spazio.=è il delimitatore chiave-valore nelle query string.
Incorporare una stringa Base64 standard in un URL richiede la percent-encoding di tutti e tre: + diventa%2B, / diventa %2F,= diventa %3D. La stringa si allunga e diventa più difficile da leggere. base64url evita la percent-encoding scegliendo caratteri già sicuri per gli URL.
Perché il padding è opzionale in base64url
Base64 standard aggiunge sempre padding all’output fino a multiplo di 4 caratteri usando =. Questo non è matematicamente necessario — il decoder può ricavare la lunghezza originale dalla lunghezza dell’output modulo 4. Il padding esiste per i decoder in streaming che devono conoscere il confine tra chunk concatenati.
Base64url omette il padding perché: (a) è ridondante, (b) = richiede la percent-encoding negli URL, e (c) i segni uguali finali negli URL a volte confondono i parser di percorso, i CDN e le chiavi di cache.
Dove viene usato ciascuno
| Caso d’uso | Variante |
|---|---|
| Allegati email (MIME) | Base64 standard |
HTTP Basic Auth (Authorization: Basic …) | Base64 standard |
Data URI (data:image/png;base64,…) | Base64 standard |
| JWT (tutti e tre i segmenti) | Base64url, senza padding |
| OAuth 2.0 PKCE code verifier/challenge | Base64url, senza padding |
| Chiavi WebPush, JOSE/JWS/JWE | Base64url, senza padding |
| Cookie firmati (Rails, Django, ecc.) | Base64url, senza padding |
Il problema della decodifica
Il decoder Base64 predefinito di molte librerie fallisce sull’input base64url. base64.b64decode di Python rifiuta -e _; Buffer.from(s, "base64")di Node.js è permissivo ma l’inverso (.toString("base64")) produce Base64 standard.
Hack cross-linguaggio: sostituire prima di decodificare.
// base64url → Base64 standard
const standard = base64url
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(Math.ceil(base64url.length / 4) * 4, "=");Meglio ancora: usare una libreria che supporti esplicitamente base64url (Node 16+ ha Buffer.from(s, "base64url"); Python ha base64.urlsafe_b64decode).
La regola pratica
Se il valore codificato apparirà in un URL, in un JWT, in un cookie firmato o come segmento di percorso — usa base64url. Se è per un allegato email, un header HTTP (diverso dai cookie), o un data URI JSON — usa Base64 standard.
Il nostro codificatore Base64gestisce entrambe le varianti, con un selettore per l’alfabeto. Il decoder JWT presuppone base64url perché lo standard lo richiede.
Dati numerici
- Lunghezza codificata: entrambe le varianti espandono il binario esattamente di 4/3 (
ceil(n/3)*4). 100 byte di input → 136 caratteri di output in Base64 standard, o 134 caratteri senza padding in base64url. - Overhead del padding: Base64 standard aggiunge 0, 1 o 2 caratteri
=per stringa codificata — al massimo un incremento del 1,5% per payload brevi, trascurabile per quelli lunghi. - Costo della percent-encoding negli URL: ogni
+ / =in Base64 standard si espande a 3 byte (%2B %2F %3D) quando codificato per URL. Per un JWT di 512 caratteri, si sprecano tipicamente 30-60 byte — base64url li evita completamente. - Dimensione dell’alfabeto: 64 caratteri in entrambe, quindi ogni carattere di output trasporta esattamente 6 bit di input.
- Dimensioni segmenti JWT (RFC 7519): header tipico 40-50 caratteri, payload 100-400 caratteri, firma HS256 43 caratteri (esattamente 32 byte / 6 = 43 caratteri base64url, senza padding).
- Throughput:
Buffer.from(s, "base64")di Node 20 decodifica a ~2 GB/s;base64.b64decodedi Python a ~600 MB/s su un laptop del 2024.
Matrice decisionale
| Dove vive il valore | Scegli |
|---|---|
| Percorso URL o query string | base64url, senza padding |
| JWT (header / payload / firma) | base64url, senza padding (RFC 7519) |
| OAuth 2.0 PKCE verifier | base64url, senza padding (RFC 7636 §4.1) |
| Cookie firmato (Rails / Django / Express) | base64url, senza padding |
URI data: in HTML/CSS | Base64 standard con padding |
| Allegato email MIME | Base64 standard con padding, a capo ogni 76 caratteri (RFC 2045) |
| Header HTTP Basic Auth | Base64 standard (RFC 7617) |
| Chiavi sottoscrizione WebPush | base64url, senza padding (RFC 8291) |
Fonti
- RFC 4648 — The Base16, Base32, and Base64 Data Encodings — rfc-editor.org/rfc/rfc4648 (§4 standard, §5 URL-safe).
- RFC 7519 — JSON Web Token (JWT), impone base64url senza padding — rfc-editor.org/rfc/rfc7519.
- RFC 7636 — OAuth 2.0 PKCE, §4.1 specifica la challenge SHA-256 codificata in base64url — rfc-editor.org/rfc/rfc7636.
Frequently asked questions
- Posso decodificare base64url con un decoder Base64 standard?
- A volte — Buffer.from di Node è permissivo, b64decode di Python non lo è. L'approccio sicuro per ogni linguaggio: sostituire '-' con '+', '_' con '/', e aggiungere '=' fino a multiplo di 4 prima della decodifica. I runtime moderni offrono decoder base64url espliciti (Node 16+, urlsafe_b64decode di Python) che gestiscono questo direttamente.
- Perché base64url elimina il padding?
- Tre motivi: è matematicamente ridondante (i decoder possono ricavare la lunghezza originale dalla lunghezza dell'output mod 4), il carattere '=' richiede la percent-encoding negli URL, e i '=' finali confondono alcuni parser di percorso, CDN e chiavi di cache.
- I segmenti JWT sono sempre in base64url?
- Sì. RFC 7519 impone base64url senza padding per tutti e tre i segmenti JWT (header, payload, firma). Codificare un JWT con Base64 standard produce un token che non verrà verificato da nessuna libreria conforme.
- Base64url risparmia byte rispetto a Base64 standard?
- Marginalmente — stesso numero di caratteri dell'alfabeto significa stessa lunghezza codificata prima del padding. Eliminare il padding risparmia 0-2 byte per codifica. Il vero risparmio è evitare la percent-encoding (ogni %XX sono 3 byte per 1), che può risparmiare 6-9 byte per stringa Base64 in un URL.
Related
Published May 16, 2026