> ## Content Index
> Fetch the complete content index at: https://www.tool-cloud.com/blog/it/llms.txt
> Use this file to discover other available public pages before exploring further.

# Come convertire testo in Base64 con JavaScript (e decodificarlo)
- URL: https://www.tool-cloud.com/blog/it/come-convertire-testo-in-base64-con-javascript-e-decodificarlo/
- Published: 2026-10-14T18:18:34.000Z
- Updated: 2026-10-14T18:28:55.000Z
- Description: La riga di codice che funziona, quella che genera un errore e il motivo; oltre al round-trip sicuro per UTF-8, i metodi nativi del 2025 e le differenze in Node.js.
- Author: Tool Cloud Team
- Tags: Sviluppatori, Guide

Per codificare in Base64 una stringa in JavaScript, chiama `btoa('Hello, world!')`, che restituisce `'SGVsbG8sIHdvcmxkIQ=='`. Questa riga di codice funziona solo per testo ASCII e Latin-1: nel momento in cui passi un emoji, un accento o un carattere cinese, genera `InvalidCharacterError`. Questa guida mostra il motivo di questo comportamento e il round-trip sicuro per UTF-8 che lo risolve, inclusi i nuovi metodi nativi del 2025.

## Base64 in una riga: btoa() e atob()

La risposta più veloce: codifica con `btoa('Hello, world!')`, decodifica con `atob('SGVsbG8sIHdvcmxkIQ==')`. Entrambi verificati: `btoa('a')` restituisce `'YQ=='`, e `atob('SA==')` restituisce `'S'`.

Ciò che [btoa()](https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa) fa realmente è trattare la tua stringa come una "stringa binaria", ovvero una stringa in cui ogni carattere rappresenta esattamente un byte di dati binari, per poi codificare quei byte in Base64\. Il caso d'uso segnalato da MDN riguarda dati che causano problemi di trasmissione, come i caratteri di controllo ASCII (0–31), che vuoi far passare attraverso un canale solo testuale.

Questa riga di codice è sufficiente quando il tuo testo è ASCII: il code point di ogni carattere deve essere inferiore a 256 e, secondo MDN, la corrispondenza tra byte e code point vale in modo affidabile solo fino a 0x7F. L'intervallo 0x80–0xFF funziona tecnicamente, ma si basa sul comportamento di codifica Latin-1 che non è portabile tra ambienti diversi. Una buona regola: tutto ciò che non è ASCII richiede il percorso UTF-8 descritto di seguito.

## Perché btoa() genera InvalidCharacterError su emoji, accenti e CJK

Base64 è una codifica binaria, non una codifica di testo. [Il glossario di MDN](https://developer.mozilla.org/en-US/docs/Glossary/Base64) definisce la pratica di passare testo arbitrario o HTML a `btoa()` un malinteso comune: `btoa`/`atob` sono anteriori agli array tipizzati, quindi usano le stringhe per rappresentare i byte. I problemi iniziano quando la tua stringa non ha un byte per carattere.

Le stringhe JavaScript sono UTF-16, quindi un'unità di codice è sempre due byte e i caratteri fuori dal BMP (come gli emoji) occupano quattro byte come una coppia di surrogate; UTF-8 usa da uno a quattro byte per code point. `btoa()` e `atob()` funzionano solo con caratteri a byte singolo (intervallo Latin-1). Prova a codificare qualcosa di più ampio e otterrai esattamente questo errore:

```
DOMException: Failed to execute 'btoa' on 'Window':
The string to be encoded contains characters outside of the Latin1 range

```

Il caso problematico è banale da riprodurre: `btoa('✓')` genera un errore, così fa `btoa('a Ā 𐀀 文 🦄')`. Emoji, lettere accentate oltre Latin-1 e caratteri CJK si trovano tutti sopra 0xFF, quindi nessuno di essi si adatta all'assunzione di un byte per carattere. `'✓'` è U+2713, ben al di sopra di 0xFF, quindi `btoa()` rifiuta piuttosto che corrompere silenziosamente i tuoi dati. Questo rifiuto è probabilmente la caratteristica migliore dell'API: fallisce in modo evidente invece di produrre mojibake che scopriresti settimane dopo.

Un'altra cosa utile da sapere prima di risolvere questo problema: Base64 rende i dati più grandi, circa del 33% rispetto ai byte grezzi. Va bene per stringhe brevi, token e URI dei dati, ma è un motivo valido per non codificare in Base64 payload grandi: codifica al confine in cui ne hai bisogno, non ovunque.

![Un nastro blu 3D morbido che scorre da sinistra dividendosi in due percorsi: un filamento forma gruppi ordinati e raggruppati di…](https://www.tool-cloud.com/blog/it/content/images/2026/10/javascript-base64-encode-utf8-split.webp)

Un flusso di dati, due impronte di byte: cluster multi-byte contro byte singoli.

### Il caso limite delle surrogate isolate (e String.isWellFormed())

La maggior parte degli articoli salta questo punto, ma causa problemi: una stringa UTF-16 contenente una surrogate isolata (metà di una coppia di emoji, spesso dovuta a un bug di slicing) è "malformata". Secondo [la guida di web.dev](https://web.dev/articles/base64-encoding), `TextDecoder` sostituisce silenziosamente U+FFFD per impostazione predefinita. Prima della codifica, verifica la conformità con `String.prototype.isWellFormed()`, oppure intercetta l'errore `URIError` che `encodeURIComponent()` genera sulle surrogate isolate, quindi decidi esplicitamente: rifiutare i dati, abilitare la sostituzione deliberatamente o generare un errore per analisi successive.

## Il metodo corretto: TextEncoder → byte UTF-8 → Base64

La soluzione corretta consiste nel convertire la tua stringa in byte UTF-8 con `TextEncoder` prima, poi codificare in Base64 i byte invece della stringa. `new TextEncoder().encode(str)` trasforma qualsiasi stringa JavaScript in un `Uint8Array` di byte UTF-8 (TextEncoder parla solo UTF-8). Sul lato decodifica, `new TextDecoder()` usa UTF-8 per impostazione predefinita e lo inverte esattamente. Nota cosa ti restituisce `atob()` qui: una stringa binaria, un carattere per byte, non testo UTF-8\. Ecco perché il lato decodifica necessita di `TextDecoder` per trasformare i byte nuovamente in testo leggibile.

### Helper bytesToBase64() e base64ToBytes() (codice copia-incolla)

Ecco [il round trip canonico di MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa), che funziona in ogni browser che supporta `TextEncoder` (IE escluso):

```js
function bytesToBase64(bytes) {
  const binString = Array.from(bytes, (b) => String.fromCodePoint(b)).join('');
  return btoa(binString);
}

function base64ToBytes(base64) {
  const binString = atob(base64);
  return Uint8Array.from(binString, (m) => m.codePointAt(0));
}

```

### Verifica con un round trip completo

Codificare una stringa diventa una riga:

```js
const encoded = bytesToBase64(new TextEncoder().encode('a Ā 𐀀 文 🦄'));
// 'YSDEgCDwkICAIOaWhyDwn6aE'

```

La decodifica la inverte:

```js
const decoded = new TextDecoder().decode(base64ToBytes(encoded));
// 'a Ā 𐀀 文 🦄'

```

Il round trip verificato da web.dev conferma lo stesso pattern: `'hello⛳❤️🧀'` codifica in `'aGVsbG/im7PinaTvuI/wn6eA'` e la decodifica restituisce esattamente l'originale, emoji inclusi.

### Salta il trucco unescape(encodeURIComponent())

Troverai `btoa(unescape(encodeURIComponent(str)))` copiato ovunque. Funziona, ma `unescape()` è un'eredità dell'Annesso B, deprecato dall'ECMAScript 3\. Il nuovo codice dovrebbe usare `TextEncoder` al suo posto: è esplicito, standardizzato e non dipende da una globale deprecata.

## Il modo del 2025: Uint8Array.toBase64() e Uint8Array.fromBase64()

Da settembre 2025, JavaScript dispone di metodi Base64 nativi direttamente su `Uint8Array`, quindi gli helper per stringhe di byte sopra non sono più l'unica opzione. La [proposta Base64 di TC39](https://github.com/tc39/proposal-arraybuffer-base64/blob/main/README.md) ha raggiunto lo Stage 4 ed è parte del linguaggio; la motivazione di TC39 è che base64 non è una codifica di testo, quindi TextEncoder/TextDecoder sono concettualmente lo strumento sbagliato per questo scopo.

Secondo [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global%5FObjects/Uint8Array/toBase64), queste sono funzionalità "Appena disponibili" nella baseline dei browser evergreen. Verifica i runtime di destinazione prima di rilasciare: se supporti browser o versioni Node più vecchie, mantieni il fallback `TextEncoder` \+ `btoa`. MDN preferisce questi metodi rispetto a `btoa()`/`atob()` quando hai già un `Uint8Array`, perché non è necessario creare una stringa di byte grezzi.

### Codifica: bytes.toBase64()

```js
const b64 = new TextEncoder().encode('Hello, world!').toBase64();
// 'SGVsbG8sIHdvcmxkIQ=='

```

Una conversione UTF-8, una chiamata al metodo, fatto.

### Decodifica: Uint8Array.fromBase64()

```js
const text = new TextDecoder().decode(Uint8Array.fromBase64(b64));
// 'Hello, world!'

```

MDN segnala che `fromBase64()` dovrebbe essere preferito a `atob()` perché restituisce un vero array di byte, a meno che i dati decodificati non siano intesi come testo ASCII. Accetta solo l'alfabeto base64 (A–Z, a–z, 0–9, più `+/` o `-_`), il padding `=` opzionale e ignora gli spazi bianchi ASCII; genera un errore `SyntaxError` per caratteri esterni all'alfabeto.

### Opzioni: alphabet, omitPadding, lastChunkHandling

Il [pacchetto di opzioni della specifica](https://tc39.es/proposal-arraybuffer-base64/) ti dà un controllo preciso:

| Opzione           | Valori                                             | Cosa fa                                                                                    |
| ----------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| alphabet          | 'base64' (default), 'base64url'                    | +// vs \-/\_                                                                               |
| omitPadding       | booleano                                           | Rimuove il trailing \=: new Uint8Array(\[72\]).toBase64() → 'SA==', con omitPadding → 'SA' |
| lastChunkHandling | 'loose' (default), 'strict', 'stop-before-partial' | Tolleranza del decoder per l'ultimo chunk                                                  |

`'loose'` tollera la mancanza di padding e spazi bianchi. `'strict'` richiede chunk esatti da 4 caratteri con bit di overflow azzerati, che è ciò che vuoi quando validi input non attendibili. `'stop-before-partial'` è per lo streaming. La modalità loose predefinita tollera anche bit di overflow non nulli, che la modalità strict rifiuta.

## Base64 vs base64url: quale alfabeto ti serve?

La base64 standard usa `+` e `/` più il padding `=`, e va bene per gli URL `data:` poiché questi non hanno segmenti di percorso o query string. base64url (RFC 4648 §5) sostituisce `-` e `_` e solitamente omette il padding, ed è richiesta ogni volta che l'output viaggia all'interno di un URL: ad esempio, i JWT usano base64url per tutti e tre i loro segmenti.

### Quando usare base64url

Usa base64url quando la stringa codificata diventa parte di un percorso URL o di un parametro query, dove `+` e `/` richiederebbero l'escaping (e il padding `=` appesantisce le query string). Usa la base64 standard per i data URI, per incorporare byte nei payload JSON o per qualsiasi cosa che non sia un URL.

### Come produrre base64url

Tre modi, a seconda dell'ambiente:

- Browser moderni: `bytes.toBase64({ alphabet: 'base64url' })`
- Node 16+: `Buffer.from(str).toString('base64url')`
- Post-processing: prendi l'output standard e sostituisci `+` → `-`, `/` → `_`, poi rimuovi il `=`

Una sottigliezza da conoscere: RFC 4648 richiede agli encoder di azzerare i bit di overflow non dati nell'ultimo chunk, e i decoder strict potrebbero generare errori se non sono azzerati. Il glossario di MDN fornisce l'esempio di `'VR=='`, che è lo stesso byte di `'VQ=='` con bit di padding sporchi. Genera base64 con un encoder ben comportato e questo problema non si presenterà; se ne scrivi uno tuo, potrebbe presentarsi.

## Codificare un file o un'immagine in Base64 (data URLs)

Per i file, la via del browser è `FileReader` con `readAsDataURL()`, che produce un full data URL, poi rimuovi il prefisso per ottenere il base64 grezzo. Per le immagini già renderizzate in un canvas, `toDataURL('image/png')` lo fa in una sola chiamata.

### FileReader.readAsDataURL() → data URL → base64 grezzo

```js
function fileToDataURL(file) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result);
    reader.readAsDataURL(file);
  });
}

const dataUrl = await fileToDataURL(fileInput.files[0]);
const rawBase64 = dataUrl.replace(/^data:\w+\/\w+;base64,/, '');

```

### canvas.toDataURL() per le immagini

Se hai disegnato un'immagine su un canvas (resize, crop, ecc.), salta FileReader completamente:

```js
const dataUrl = canvas.toDataURL('image/png');

```

Vale la stessa rimozione del prefisso se ti serve la stringa base64 nuda e cruda.

### Data URL ai byte

La direzione inversa è un fetch più un array buffer. L'esempio di MDN: un data URL costruito da `new Uint8Array([0,1,2])` è `'data:application/octet-stream;base64,AAEC'`, e puoi recuperare i byte in questo modo:

```js
const res = await fetch(dataUrl);
const bytes = new Uint8Array(await res.arrayBuffer());
// [0, 1, 2]

```

Ed è il promemoria per cui esiste questo articolo: non chiamare `btoa()` su testo o HTML arbitrari per costruire data: URL. È un encoder byte-to-byte e l'input non Latin-1 genererà un errore.

## Node.js: Buffer.from(str, 'base64') — e la sua unica differenza pericolosa

Node ha i suoi idiomi che saltano completamente il passaggio con TextEncoder: `Buffer.from('Hello, world! 🌍').toString('base64')` gestisce Unicode automaticamente, perché Buffer lavora sui byte fin dall'inizio. Per decodificare usa `Buffer.from(encoded, 'base64').toString('utf8')`.

Altre forme utili: `Buffer.from(str).toString('base64url')` (Node 16+), e interi file tramite `fs.readFileSync('image.png').toString('base64')`.

```js
const encoded = Buffer.from('Hello, world! 🌍').toString('base64');
const decoded = Buffer.from(encoded, 'base64').toString('utf8');

```

### Node non valida la tua base64 (fallimenti silenziosi)

Ecco la parte che sorprende le persone: il decoder base64 di Node non valida l'input. `Buffer.from('!!!!', 'base64').toString()` restituisce `''` senza errori, e `Buffer.from('aGVsbG8!!!', 'base64')` scarta silenziosamente la spazzatura e restituisce `'hello'`. Confrontalo con `atob('aGVsbG8!!!')`, che genera `InvalidCharacterError`. Se la base64 proviene da utenti, da un'API o da terzi, validala prima di decodificarla in Node, oppure usa `Uint8Array.fromBase64()` con `lastChunkHandling: 'strict'` dove i runtime lo supportano.

### btoa/atob sono deprecati? Browser vs Node

Qui le fonti si contraddicono davvero, quindi ecco una risposta onesta. Nei browser, [MDN segna btoa() e atob()](https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa) come ampiamente disponibili nella Baseline da luglio 2015, non deprecati. In Node, le [type definitions](https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/58795) li segnano come `@deprecated` con il commento che "non dovrebbero mai essere usati in nuovo codice", raccomandando invece `Buffer.from(str, 'base64')`.

Una buona regola: è un'API legacy, non rimossa. Va bene nel codice del browser, preferisci `Buffer` nel codice specifico per Node. Per consenso della community (non una garanzia della specifica), `btoa`/`atob` sono anche la scelta pratica cross-environment se ti serve un unico percorso di codice che giri invariato in browser, Deno e Node senza dipendenze.

## Cosa usare effettivamente

Se prendi un solo consiglio da questo articolo, prendi le tre righe di codice qui sotto. Mantieni `btoa`/`atob` per ASCII puro e codebase legacy, e usa la funzione helper `bytesToBase64` come fallback per il browser, finché `toBase64()` non copre tutto il tuo pubblico.

```js
// Modern browsers (2025+)
const enc = new TextEncoder().encode(str).toBase64();
const dec = new TextDecoder().decode(Uint8Array.fromBase64(b64));

// Browser fallback (MDN helper)
const encFallback = bytesToBase64(new TextEncoder().encode(str));

// Node (validate input first if it isn't yours)
const encNode = Buffer.from(str).toString('base64');

```

Prima di rilasciare, verifica il tuo output con un rapido round trip in uno strumento nel browser. Incolla `'Hello, world!'` nel [Encoder / Decoder Base64 gratuito di Tool Cloud](https://www.tool-cloud.com/it/dev/base64%5Fencoder/), conferma di ottenere `'SGVsbG8sIHdvcmxkIQ=='`, poi decodificalo di nuovo e controlla che sia identico. Esistono anche un [convertitore Text to Base64](https://www.tool-cloud.com/it/general/text%5Fto%5Fbase64/) e un [convertitore Base64 to Text](https://www.tool-cloud.com/it/general/base64%5Fto%5Ftext/) autonomi; i file vengono eliminati automaticamente e non serve installare nulla.

## Domande frequenti

### Perché btoa() fallisce con gli emoji ma funziona con il testo inglese?

btoa() tratta ogni carattere come un singolo byte e richiede che ogni code point sia inferiore a 256 (in modo affidabile solo fino a 0x7F). Emoji, caratteri accentati e testo CJK richiedono più byte, quindi generano un DOMException: InvalidCharacterError. Converti prima in byte UTF-8 con TextEncoder, poi codifica i byte.

### btoa() è deprecato?

Nei browser no — MDN segnala btoa() e atob() come Baseline ampiamente disponibile da luglio 2015\. Tuttavia, le definizioni dei tipi di Node li contrassegna come deprecati a favore di Buffer.from(str, 'base64') nel codice specifico per Node. Si tratta di un'API legacy, non rimossa.

### Come codifico in Base64 il testo Unicode (accenti, cinese, emoji)?

Usa new TextEncoder().encode(str) per ottenere i byte UTF-8, poi codifica i byte (btoa() tramite un helper per stringhe di byte, o Uint8Array.toBase64() nei browser moderni). Per decodificare, usa Uint8Array.fromBase64() o atob() per riottenere i byte, poi new TextDecoder().decode(bytes).

### Qual è la differenza tra base64 e base64url?

base64url (RFC 4648 §5) usa '-' e '\_' invece di '+' e '/', e solitamente omette il padding '='. Usa base64url quando l'output finisce negli URL o nei JWT; il base64 standard va bene per i data: URL, che non hanno segmenti di percorso o query string.

### Perché Node.js non genera un errore su un Base64 non valido?

Buffer.from(str, 'base64') di Node non convalida l'input: ignora silenziosamente i caratteri non validi, quindi Buffer.from('aGVsbG8!!!', 'base64').toString() restituisce 'hello' senza errori. Nei browser, atob() genera InvalidCharacterError sullo stesso input. Convalida sempre il Base64 non attendibile prima di decodificarlo in Node.