HTML Steganography Extractor
Find hidden messages in HTML, SVG and XML files made with the HTML Steganography Hider. Auto-Detect tries all six extraction methods, and Deep HTML Analysis lists zero-width characters, comments and whitespace in any file. CTF flag detection and AES-256-GCM decryption. Processed in your browser, not uploaded.
For SVG, XML and XHTML files, keep the original file name (.svg, .xml, .xhtml) or its XML declaration when extracting. The Extractor uses it to decide which extraction rules apply, the same way the Hider did.
Drop HTML file or click to browse
.html, .htm, .svg, .xml — max 50 MB
How to Find a Hidden Message in an HTML File (5 steps)
- Upload a suspicious HTML file or paste the HTML source
- Click Auto-Detect — the tool tries all 6 extraction methods
- Review the results table — look for Plaintext or File output
- If encrypted, enter the password to decrypt
- Copy the extracted message or download the hidden file
HTML Steganography Methods — Detection Guide
| Method | CTF Frequency | Detection Risk | Capacity |
|---|---|---|---|
| Zero-Width Characters | Very High | Visible in a character-level view | High |
| HTML Comments | High | Visible in View Source | Medium |
| Whitespace / Newline | Medium | Visible with show-whitespace | Medium |
| Attribute Order | Low | Visible by comparing with the original | Low |
| Case Variation | Low | Visible by comparing with the original | Low |
| Entity Substitution | Rare | Visible by comparing with the original | Low |
Detection Risk describes how visible the change is under the inspection methods named in the row. It is a qualitative guide, not a measured detection rate, and no detection tool was tested against any method. CTF Frequency is a qualitative estimate of how often a method turns up in typical CTF steganography challenges; it is not based on a statistical survey or a measured dataset.
What the Extractor Reads
The input is the source of an HTML, HTM, SVG or XML file (up to 50 MB; paste it or upload it). The Extractor reads data written by this site's Hider: a 4-byte length followed by the data, stored one bit per slot with one of six methods. It does not decode data made by other tools, which use their own framing, and it does not look inside images or other binary files.
Auto-Detect reads the file once per method (Zero-Width, Comments, Whitespace, Attribute Order, Case Variation, Entity Substitution) and lists every attempt. The Result of each row is a category of what was recovered (plaintext, file, encrypted, noise or empty), not a probability or a measure of how reliable the detection is. To choose the best row, Auto-Detect ranks the attempts: a result that decrypted with your password first, then plaintext, then a recognised file, then encrypted, then noise; rows that tie keep the table order, and an empty row never wins. The table keeps every row so you can compare them.
Choosing a method by hand is useful when you know how the file was made. A manual run reads only that method.
Reading the Anomaly Score
The anomaly score is a simple count, not a detector. It adds four parts: zero-width characters (U+200B, U+200C, U+200D), comments made only of one or two spaces, the share of lines that end in a space or tab, and numeric entities for & < > " and the non-breaking space. A page can score zero and still carry a message, as the table shows for Attribute Order and Case Variation.
Pages in Persian, Hindi and other scripts use joiners on purpose, and pages with emoji sequences use U+200D, so a nonzero score on such a page is expected. Treat the score as a reason to open the analysis tabs, not as a verdict.
Passwords, Reed-Solomon and Damaged Data
If the data has the layout of an encrypted payload the Extractor says so and asks for the password. An incorrect password and damaged data cannot be told apart, because decryption either passes its authentication check or it does not. A password you give for data that is not encrypted is ignored, and the result says it was ignored. Decryption is AES-256-GCM with a key derived by PBKDF2 (SHA-256, 100,000 iterations); encryption adds 48 bytes, the same figures as on the Hider page.
Reed-Solomon data announces itself with a 16-byte header, so you do not choose a level. The header is not itself error-corrected. The original length is stored twice, and if the two copies disagree the one that matches the amount of data present is used. If the RSC2 marker is damaged the data is not recognised as Reed-Solomon, nothing is repaired, and the bytes are shown as they are (usually as noise). If the marker is intact but the version byte is not 1, the result reports a block that could not be repaired and shows the bytes as found. Data written by this Hider never starts with the marker unless Reed-Solomon was used, because the stored data begins with the file-name flag byte. The result reports how many bytes were repaired, or how many blocks were beyond repair, in which case the bytes are shown as found and may be wrong.
Finding Hidden Characters in a File You Did Not Make
The Deep HTML Analysis tabs list what is in the file without trying to decode it: zero-width characters with their positions, every comment (flagging single or double space comments and comments over 200 characters), every line ending in whitespace, every non-ASCII character with its code point and count, every tag with two or more attributes and whether they are in alphabetical order, and the entropy of scripts, styles and text.
Export Clean HTML is not a complete steganography remover. It saves a copy without U+200B, U+200C, U+2060 and U+FEFF in text and comments; it does not undo Comments, Whitespace, Attribute Order, Case Variation or Entity Substitution, it keeps U+200D, and a message hidden any other way stays in the copy. Removing these characters can alter legitimate text, so review the cleaned output before using it, especially for Persian, Hindi and other scripts and for emoji. To check text that is not HTML, use the Unicode Steganography Extractor or the Invisible Text Extractor.
Anomaly Score on the Figures Page
The figures page is a doctype, <html>, a head with <title>Test</title>, a body and 100 lines of the form <p class="note" id="n0">Item 0: Cats & dogs play</p>. The message "Hi" was hidden with each method by the Hider and the result scored by the Extractor.
| File | Score | Label | Auto-Detect finds |
|---|---|---|---|
| The page as it is | 0 | None | nothing |
| Zero-Width Characters | 20 | Low | Hi |
| HTML Comments | 25 | Medium | Hi |
| Whitespace / Newline | 25 | Medium | Hi |
| Attribute Order | 0 | None | Hi |
| Case Variation | 0 | None | Hi |
| Entity Substitution | 20 | Low | Hi |
How the Score Is Built
| Part | Counts | Points |
|---|---|---|
| Zero-width characters | U+200B, U+200C, U+200D | 1 to 10 characters: 10; 11 to 100: 20; more than 100: 30 |
| Comments | Comments made only of one or two spaces | 5 each, at most 25 |
| Whitespace | Lines that end in a space or tab (a carriage return before the line feed is ignored) | floor(50 x lines that end in a space or tab / all lines), at most 25. Lines are the pieces left after splitting at line feeds, so a final empty line counts. |
| Entities | & < > "   (the decimal forms only; hexadecimal forms such as & are not counted) | 4 each, at most 20 |
| Label | Total of the four parts (at most 100) | None: 0; Low: 1 to 20; Medium: 21 to 40; High: 41 to 70; Very High: above 70 |
Auto-Detect Table Columns
| Column | Values | Meaning |
|---|---|---|
| Method | The six methods | One row per method, always in the same order |
| Result | plaintext, file, encrypted, noise, empty | A category, not a probability. plaintext: readable UTF-8 text. file: a stored file name, or a recognised file type in bytes that are not readable text. encrypted: the encrypted layout (a length field that matches an encrypted payload), or, marked "entropy only", bytes above 7.0 bits per byte, which compressed or random data also reaches, so that case is a hint and not proof of encryption. Only a passed password check confirms decryption. noise: bytes that are none of these. empty: no valid length was found. |
| Entropy | 0 to 8 | Shannon entropy of the recovered bytes, in bits per byte; a dash when nothing was recovered |
| Flags | Text or dash | Strings shaped like FLAG{...}, HTB{...}, picoCTF{...}, CTF{...}, flag{...} or any two to eight capital letters followed by braces |
| Action | View button | Shows up to 2,000 characters of that row's text |
File Types Recognised in the Recovered Bytes
These are the six file types the Extractor recognises (seven byte patterns, because MP3 has two) and no others. A match identifies a possible file type from the opening bytes only; it does not mean the recovered file is complete, undamaged or valid. Signatures are checked only when the bytes are not readable text: a message such as "GIF party at 5" is shown as text.
| Type | First bytes |
|---|---|
| JPEG image | FF D8 FF |
| PNG image | 89 50 4E 47 |
| GIF image | 47 49 46 |
| ZIP archive | 50 4B 03 04 |
| PDF document | 25 50 44 46 |
| MP3 audio | FF FB, or 49 44 33 (ID3 tag) |
Examples
Input: The figures page with "Hi" hidden by Zero-Width Characters, Auto-Detect, no password.
Result: Zero-Width is plaintext "Hi"; the other five rows are empty. The score is 20 (Low): the page had no zero-width characters before, the message adds 56 (4 length bytes + 1 flag byte + 2 message bytes = 7 bytes = 56 bits, one character each), 56 falls in the 11 to 100 band worth 20 points, and the other three parts are 0.
Meaning: Only the method that was used finds a valid length. The other five find no length, or none that fits.
Input: The same page before anything was hidden.
Result: All six rows are empty and the score is 0 (None).
Meaning: No message is invented from a page that has none; a pasted copy that lost the characters looks the same, so this does not prove the original was clean.
Input: The figures page with the 15-character message flag{html_demo} hidden by Zero-Width.
Result: The flag is listed as a flag and the score is 30 (Medium). 15 message bytes + 1 flag byte + 4 length bytes = 20 bytes = 160 hidden characters; more than 100 earns the top zero-width band of 30 points, and the other three parts are 0, so the total is 30.
Meaning: The score grows with the number of zero-width characters, which grows with the message, in steps of 10, 20 and 30 points.
Input: The figures page with "Hi" encrypted with the password right, opened with the password wrong.
Result: Zero-Width reads 51 stored bytes, flagged as encrypted, with the note that the password did not open them. No text is shown.
Meaning: The 51 bytes are 3 bytes of data plus 48 bytes of encryption. You see ciphertext, never a guess at the message.
Input: The figures page with "Hi" hidden without a password, opened with the password x.
Result: The result is "Hi" with a note that the password was not used.
Meaning: The data was never encrypted, so the password does not apply.
Input: 100 bytes of text on the figures page with Zero-Width and rs-20; 5 of the hidden characters are changed from one bit value to the other.
Result: The 100 bytes come back exactly; Reed-Solomon reports 4 bytes repaired and no block beyond repair.
Meaning: Five changed characters fell in 4 bytes. Reed-Solomon corrects a changed byte, not a missing character.
Input: The figures page with the message "GIF party at 5" hidden by Zero-Width, Auto-Detect.
Result: The row is plaintext "GIF party at 5", not an image file.
Meaning: File signatures are checked only on bytes that are not readable text, so a message that starts with GIF, ID3 or %PDF is still shown as a message.
Input: A Persian page with 12 copies of a word that contains a zero-width non-joiner.
Result: The score is 20 (Low) and every Auto-Detect row is empty.
Meaning: Real text can score above zero with nothing hidden in it.
How the HTML Extractor Works
1. Split the file into regions
The same scanner the Hider uses separates tags, comments, CDATA, declarations, script and style content, and text, so each method reads exactly the places it can write.
2. Read the bits of one method
Zero-Width reads U+200B and U+200C in text, comments and CDATA (for SVG and XML, only inside the root element). The Hider writes them only into text; the Extractor reads all three places because the Hider first removes any U+200B or U+200C already in text, comments or CDATA, so none of those can be mistaken for message bits. Comments reads comments made of one or two spaces. Whitespace reads the last character of each line. Attribute Order compares the first two attribute names of each qualifying tag. Case Variation checks whether each tag name is lower case. Entity Substitution checks whether each entity is named or numeric.
3. Read the length, then the data
The first 32 bits are the data length in bytes. If it is zero, over 50 MB, or longer than the bits available, the method has found nothing.
4. Undo Reed-Solomon, then encryption
If the data starts with the 4-byte Reed-Solomon marker (RSC2, or the older RSEC), it is repaired first, then decrypted if you gave a password (the Hider encrypts first and adds Reed-Solomon last, so the Extractor undoes them in the opposite order), and the file-name flag byte comes off last, keeping the name if there is one.
5. Describe what came back
The bytes are checked for UTF-8 text, for the file types in the table above, and for flag patterns. The page reports text, a file to download, or bytes that look encrypted or random.
What the results mean
| Result | What it means | What to do |
|---|---|---|
| Method in the status line | The method that produced the shown result. In Auto-Detect it is the best row of the table. | If it is not the one you expected, open the table and view the other rows. |
| Decrypted note | The authentication check passed with your password, so the bytes are the original data. | Nothing to do. |
| Password did not open it | The data has the encrypted layout and your password failed. The bytes shown are still ciphertext. | Retype the password. Damaged data gives the same message. |
| Encrypted layout, no password | The length field matches an encrypted payload and you gave no password. | Enter the password and extract again. |
| Password not used | The data is not encrypted. | Nothing to do. |
| Reed-Solomon note | Bytes repaired, or blocks beyond repair (shown as found, possibly wrong), or a header with no repair needed. | If blocks are beyond repair, try the original file instead of a copy. |
| Anomaly score and its parts | The four-part count described above, with its label. | A low score does not mean the page is clean. |
| Flags | Strings shaped like known flag formats in the recovered text. | Check them against the challenge: the pattern also matches ordinary text such as ABC{x}. |
| Text or file output | Text is shown with Copy. A file is offered for download under its stored name, with a preview for images. | If the type is wrong, the name's extension still decides how it opens. |
| String Extractor | Printable runs of at least four characters in bytes that are not text. | Look for names, URLs or flags inside a binary payload. |
| Auto-Detect table | One row per extraction method with Result, Entropy, Flags and a View button. Result describes the recovered data category, not a confidence score. | Compare rows when the best one looks wrong. |
| Deep HTML Analysis tabs | Zero-Width, Comments, Whitespace, Unicode, Attr Order and Entropy lists for the whole file. | Use them on files that were not made by this site. |
| Watermark Attribution | Matches an ID in the recovered text against a key file from the batch export, exactly or with a small bit difference. | Keep the key file private; it names every recipient. |
Limitations
- Only data written by this site's Hider is decoded. Other tools' hidden data may show up in the analysis tabs but will not decode.
- Unencrypted data has no checksum, so by design Auto-Detect cannot prove that a short result is a message and not a chance reading of the page's own characters. Such a result is shown as noise or low confidence, not as a fact.
- The score and the tabs are counts. They do not know what is normal for your page.
- A pasted copy may have lost trailing whitespace, line endings, comments or invisible characters. Whitespace and Case Variation are the most fragile (see the measured table on the Hider page). Upload the original file when you can.
- Reed-Solomon repairs changed bytes, not removed ones. Its own 16-byte header and the 4-byte length are not repaired: a damaged RSC2 marker means the data is not recognised as Reed-Solomon, and damage to the header beyond the doubled length can make the blocks unreadable.
- SVG, XML and XHTML files (names ending in .svg, .xml, .xhtml, or starting with an XML declaration or an <svg> root) use the stricter XML rules of the Hider: Case Variation does not exist there, Entity Substitution ignores , Zero-Width is read only inside the root element and Whitespace only on lines that end in a tag. A file the Hider made under one set of rules is read under the same rules only if it is recognised as the same format: keep the original file name or the XML declaration (the upload step says so too).
- No detection tool was compared against the score, and individual websites, editors and email clients were not tested.
Privacy
The file is read and analysed in your browser tab; the tool's own code does not upload your HTML. Usage statistics may record which options were used and coarse facts such as sizes, never your content. The file is never run: it is shown as text and analysed, not opened as a page. Fetch URL is the exception that talks to another website: your browser sends the request straight to that site, which can see your IP address like any visitor, and it works only if that site allows other sites to read it (CORS). The response does not pass through StegoToolkit's servers and the address is not recorded in the usage statistics; if the fetch fails you are told why, and you can download the file and upload it or paste its source instead.
Which Tool Next?
- HTML Steganography Hider — make a file to test the Extractor with.
- Unicode Steganography Extractor — plain text with zero-width characters, variation selectors or Cyrillic look-alikes.
- Invisible Text Extractor — plain text with zero-width characters and platform-aware character sets.
- Whitespace Steganography Extractor — trailing space and tab patterns in text files.
Frequently Asked Questions
How do I know if an HTML file has hidden data?
Upload or paste the HTML — the tool shows a Zero-Width Character count and an Anomaly Score (0–100). The score is a simple heuristic that counts zero-width characters, comments with only one or two spaces, lines ending in whitespace and numeric entities; it is not a detector. Pages in Persian, Hindi or with emoji legitimately contain joiners, and a low score does not prove a page is clean.
What is a zero-width character?
Zero-width characters are Unicode code points (U+200B, U+200C, etc.) that browsers draw with no width, so they do not show on the page. A code editor with a show-invisible-characters option or a Unicode inspector reveals them, and they are kept when the text is copied.
The auto-detect returned noise — what does that mean?
The extracted bytes are not valid UTF-8 text or a known file format. If they have the layout of an encrypted payload the result says so and asks for the password. Otherwise the data may be damaged, may use another method, or may not be a message from this tool at all.
Can I decode HTML files encoded by other tools?
Only files made with this site's Hider (a 4-byte length followed by the message, written with one of the six methods). Files from other tools use different framing and will not decode, although the Zero-Width tab and the anomaly score still show suspicious characters.
Is my HTML sent to a server for analysis?
No. Extraction and analysis run in your browser tab; your HTML is not sent to a server. Usage statistics may record which options were used, never your HTML.
What is the Clean HTML Export feature?
It is not a complete steganography remover. It downloads a copy of the HTML without the characters U+200B, U+200C, U+2060 and U+FEFF in the text and comments. Tags, attribute values, scripts and styles are not touched, and U+200D is kept because emoji sequences and some scripts need it. Comments, whitespace, attribute order, tag case and entity spelling are not changed, so data hidden with those methods stays. Removing these characters can alter legitimate text (Persian, Hindi and emoji use some of them), so review the cleaned output before using it.
Related Tools
HTML Steganography Hider
Hide secret messages inside HTML, SVG and XML files using zero-width characters, comments, whitespace, attribute order, case variation, and entity substitution. Optional AES-256-GCM encryption. The methods aim to keep normal browser rendering, but some can change text content or be removed by HTML processing tools.
Steganography Detector
Statistical steganalysis with three published tests — chi-square, RS analysis and Sample Pairs — fused into one score and checked against the real StegExpose on a small test set. Payload size estimate. Method fingerprint. WAV audio support. Processed in your browser, not uploaded.