Browser-only · strict RFC 3629 validation
UTF-8 Encoder Decoder
Encode text to UTF-8 hex bytes, decode hex bytes back to text, or build a percent-encoded string — and get a byte-by-byte validation report that pinpoints orphan continuation bytes, truncated sequences, overlong encodings, surrogate code points and anything past U+10FFFF.
Workbench
Result
How it works
Three directions, one strict byte-level check — all executed in this tab.
Text → hex bytes uses TextEncoder; hex bytes → text parses the digits and decodes with fatal semantics replaced by a hand-written scanner; percent-encoding walks the UTF-8 bytes and applies RFC 3986.
Hex input tolerates spaces, tabs, newlines, commas, semicolons and 0x prefixes. Bad characters and odd digit counts are reported with their exact input offsets instead of being silently dropped.
Every row carries a 0000/0010 offset ruler; malformed bytes are highlighted in the grid and match the numbered entries in the report on the right.
What the validator catches
A byte in 0x80–0xBF with no lead byte in front of it — the signature of a truncated cut in the middle of a character.
A lead byte at the end of the payload: the report names how many continuation bytes are missing and where the sequence started.
0xF8–0xFF are never valid starts — 5- and 6-byte forms were removed from UTF-8 by RFC 3629.
C0 80 for U+0000, E0 80 80, F0 80 80 80: shortest-form violations that smuggle bytes past filters.
ED A0 80–ED BF BF encodes U+D800–U+DFFF, the CESU-8/WTF-8 legacy range that is not valid UTF-8.
F4 90 80 80 and the 0xF5–0xF7 leads decode past the Unicode maximum and are rejected.
FAQ
Does anything leave my browser?
No. The page makes zero network requests: conversion and validation run in JavaScript on your machine, so the tool works offline and nothing is logged. Open DevTools → Network and you will see no activity while you type.
Why not just use TextDecoder with fatal: true?
Fatal decoding only tells you that decoding failed, and the default mode quietly swaps in U+FFFD so you cannot see which byte broke. This tool runs its own RFC 3629 state machine, so it reports the offset, the reason class and the surrounding context — the same verdict as iconv -f utf-8, with more detail.
Why is my “identical” text rejected or different?
Normalization is a separate concern: “é” can be one code point (NFC) or two (NFD) and both are valid UTF-8 while the bytes differ. The report flags non-NFC input and compares the byte counts — it never calls a normalization difference invalid.
What do the three counters mean?
Bytes is the UTF-8 length on the wire; code points is the number of Unicode scalar values; UTF-16 code units is what a JavaScript string or Java/C# char array would store (astral characters count as two). For well-formed input code points ≤ bytes and UTF-16 units ≥ code points always hold.
My paste is huge — will it freeze?
Typical payloads finish in a few milliseconds. Above 200,000 characters the page analyses the first segment and tells you so instead of stalling, and the on-screen byte grid is capped at 4,096 bytes while the Copy button still exports the full output.