Text encodings explained: UTF-8, Base64 and URL encoding

Updated:

Computers store and send bytes, not letters. Every time text is saved, sent or put into a web address, something has to decide which bytes stand for which characters, and sometimes the bytes themselves have to be rewritten so they survive the trip. Three encodings do most of that work: UTF-8 turns characters into bytes, Base64 turns any bytes into plain letters and digits, and percent-encoding makes text safe inside a URL.

This last part of the series explains each of them with examples you can check byte by byte, and links them back to the problems seen in the JSON and CSV parts: the byte order mark and accents that turn into ã.

Characters, code points and bytes

Unicode gives every character a number, called a code point and written as U+ followed by hexadecimal digits: A is U+0041, é is U+00E9, € is U+20AC and the emoji 😀 is U+1F600. The range runs from U+0000 to U+10FFFF. An encoding decides how those numbers are stored as bytes.

UTF-8, defined in RFC 3629 (an Internet Standard since November 2003), uses one to four bytes per character. The 128 ASCII characters keep their single byte, so plain English text is identical in ASCII and UTF-8; other characters take two, three or four bytes:

UTF-8 is now the default almost everywhere: W3Techs found it on 99.1% of websites whose encoding it could identify in October 2026. Other encodings still exist inside programs, though. JavaScript strings, for example, are stored as UTF-16, in 16-bit units, and MDN notes that a character above U+FFFF takes two of them. That is why the same emoji counts as 1 character on screen, 2 in JavaScript's length and 4 bytes in UTF-8, and why limits measured in characters and in bytes rarely agree.

The byte order mark

The byte order mark, U+FEFF, can be placed at the very start of a file. In UTF-8 it is always the three bytes EF BB BF. UTF-8 has no byte order to mark, so there it only works as a signature that says the file is UTF-8. The Unicode Consortium advises against it where a protocol or program expects specific characters at the start of a file.

Both earlier parts of this series meet it from opposite sides. JSON sent over a network must not start with it, and JavaScript's JSON.parse rejects text that does. Excel, on the other hand, needs it to open a UTF-8 CSV correctly by double-clicking. Whether to add it depends on which program will read the file.

Base64: any bytes as plain text

Some places only accept text: an email body, a JSON string, an HTML attribute. Base64, specified in RFC 4648 (October 2006), lets any bytes, including an image or a PDF, travel through them. It takes the input three bytes (24 bits) at a time and writes each group as four characters from an alphabet of 64: A–Z, a–z, 0–9, + and /. When the last group is short, = fills it out. These are the test vectors in the RFC:

Four characters for every three bytes means Base64 is a third larger than the original: a 3,000,000-byte file becomes 4,000,000 characters. Email (MIME) also breaks Base64 into lines of at most 76 characters, which adds a little more.

Base64 is an encoding, not encryption. Anyone can decode it, with no key, so it protects nothing. If a password or a token looks unreadable only because it is in Base64, it is not protected.

Text has to become bytes before it can be Base64-encoded, so the result depends on the text encoding. The letter é gives w6k= from its two UTF-8 bytes, but 6Q== from its single byte in Latin-1. web.dev points out that the browser's own btoa() function accepts only characters that fit in one byte and throws an error for others, such as €; text has to be converted to UTF-8 bytes first. The nTools Base64 tool encodes text as UTF-8, and when decoding it reports whether the bytes were UTF-8 or Windows-1252.

Base64 encoder / decoder

base64url: the version for web addresses

Two of the Base64 characters cause trouble outside plain text. In a URL, + can be read as a space and / separates path segments; in a file name, / is not allowed at all. RFC 4648 therefore defines a second alphabet, the URL and filename safe one, usually called base64url, that uses - and _ instead. Specifications that use it may also leave out the = padding, which RFC 4648 allows when they say so explicitly.

JSON Web Tokens are the best-known example. RFC 7515 defines their encoding as base64url with every trailing = removed and no line breaks, which is why the parts of a token can contain - and _ but never +, / or =. A decoder that only knows standard Base64 rejects such strings; the nTools tool is one of them, so replace - with + and _ with / before decoding.

Percent-encoding: text inside a URL

A URL can only contain a limited set of ASCII characters, and some of them have a job: / separates the path, ? starts the query, & separates parameters, # starts the fragment. RFC 3986 (January 2005, Internet Standard) sets the rules. Letters, digits and - . _ ~ can always be used as they are. Characters with a job are encoded when they are part of a value rather than a separator, and any byte outside the allowed set is written as % followed by two hexadecimal digits. Text is converted to UTF-8 first, so a character outside ASCII becomes several %-codes:

Encode the values you put into a URL, not the whole address: encoding https://example.com/?q=1 as one value turns its : / ? and = into codes, which is right when the address is itself a parameter of another URL and wrong when it should work as a link. JavaScript's encodeURIComponent() does exactly that per-value encoding, and, as MDN lists, it also leaves ! ' ( ) and * unencoded. The nTools URL tool uses it.

URL encoder / decoder

A plus or %20?

HTML forms send their data in a slightly different format, application/x-www-form-urlencoded, which the WHATWG URL Standard defines. In it, a space is written as +, so a search for a b&c=d is sent as q=a+b%26c%3Dd. Elsewhere in a URL, + is just a plus sign.

This explains a common mix-up: decoding a+b%20c with a general URL decoder gives a+b c, because only %20 is a space there; a form decoder gives a b c. Choose the decoder that matches where the text came from. MDN suggests replacing %20 with + after encodeURIComponent() when you need the form format.

Double encoding

RFC 3986 warns that a string must not be percent-encoded or decoded more than once. When it happens, the % of each code is encoded again as %25:

Sequences such as %2520 or %25C3%25A9 in an address are a sign that something encoded text that was already encoded. Decode once, check the result, and encode only the parts that need it.

Which one do you need?

SituationUse
Saving or sending text: files, web pages, JSON, CSVUTF-8
Putting bytes, such as an image or a file, inside text: JSON, email, HTMLBase64
Base64 inside a URL, a file name or a tokenbase64url
A value inside a URL: a search term, a parameter, a path segmentPercent-encoding
Data submitted by an HTML formForm encoding (spaces as +)
Keeping data secretNone of these: use encryption

Frequently asked questions

Is Base64 a form of encryption?

No. It is a reversible encoding that anyone can decode without a key. It makes data safe to transport as text, not safe from being read.

Why does my Base64 differ from another tool's?

Usually because the text was converted to bytes with a different encoding, such as Latin-1 instead of UTF-8, because one tool uses base64url or leaves out the = padding, or because one adds line breaks.

Why does an emoji count as two characters?

Many programs, JavaScript among them, count 16-bit UTF-16 units, and an emoji such as 😀, above U+FFFF, needs two. In UTF-8 it takes four bytes.

Should I save files as UTF-8 with a BOM?

Only when the program that reads the file needs it, as Excel does with CSV files. For JSON and most web files, save UTF-8 without a BOM.

Why does my link contain %2520?

It was encoded twice: %20, a space, was encoded again and its % became %25. Decode it once and use the result.

Do the nTools encoders upload my text?

No. Encoding and decoding run in your browser, and the text is not sent anywhere.

Previous in the series ← CSV files explained: delimiters, quotes, encoding and Excel

References

  1. RFC 3629 — UTF-8, a transformation format of ISO 10646
  2. Unicode Consortium — FAQ: UTF-8, UTF-16, UTF-32 & BOM
  3. W3Techs — Usage statistics of character encodings for websites
  4. MDN — UTF-16 (glossary)
  5. RFC 8259 — The JavaScript Object Notation (JSON) Data Interchange Format
  6. Microsoft Support — Opening CSV UTF-8 files correctly in Excel
  7. RFC 4648 — The Base16, Base32, and Base64 Data Encodings
  8. web.dev — The nuances of base64 encoding strings in JavaScript
  9. RFC 7515 — JSON Web Signature (JWS)
  10. RFC 3986 — Uniform Resource Identifier (URI): Generic Syntax
  11. WHATWG — URL Standard
  12. MDN — encodeURIComponent()

Related guides