Embedding Base64 Images in Markdown: What Works, What Gets Stripped, and Per-Platform Fixes
You dropped a data URI into a markdown file —  — and the preview shows it perfectly. Then the README looks blank on github.com, the Notion API call fails, or the image vanishes from your WordPress post the moment you hit save. Nothing is wrong with your Base64 string in most of these cases: several of the most popular markdown hosts and editors structurally refuse or strip data URIs. Here is the full compatibility picture, and what to do on each platform.
Compatibility matrix: who renders Base64 markdown images
| Renderer / platform | Renders Base64 images? | What actually happens |
|---|---|---|
| GitHub (README, issues, discussions) | No | GitHub Flavored Markdown explicitly disallows data: URIs — a spec-level ban, not a setting. |
| Notion API | No | Image blocks accept external HTTPS URLs only; no field for inline Base64. |
| Notion app (paste / upload) | Yes | Works because Notion re-hosts the file as an attachment — the data URI itself is never stored. |
| WordPress (editing & saving) | Depends | The wp_kses sanitizer strips data: from users without the unfiltered_html capability; editor round-trips can mangle long attributes. |
| WordPress (emails via SMTP/log plugins) | No | Mail plugins sanitize outgoing HTML and remove data URIs from sent messages. |
| Typora | Yes | Full local rendering — the classic home of single-file Base64 markdown. |
| Obsidian | Yes | Renders in the reading view; keep the MIME type strict (image/jpeg, not image/jpg). |
| VS Code built-in preview | Mostly | Generally works, but a long-standing parsing bug (microsoft/vscode#76080) breaks some data URIs — test before blaming your string. |
| Pandoc → standalone HTML | Yes | The data URI is just an img src in the generated HTML. |
The pattern to remember: local renderers show it, publishing platforms strip it. The same restrictions apply to email clients — see our guides on Base64 images in Outlook and Gmail signatures.
GitHub: data URIs are banned, not broken
GitHub Flavored Markdown allows only a short whitelist of URL schemes in image sources — http, https, mailto and relative paths. data: is not on the list, so a Base64 <img> or ![]() is structurally blocked on github.com: READMEs, issues, pull requests, discussions, wikis. This is the spec working as written, and no repository setting changes it. If the same file renders in Typora or VS Code but shows nothing on GitHub, that is exactly what is happening.
Fixes that work on GitHub
- Commit the image and use a relative path. In READMEs and other in-repo markdown files,
resolves against the file's location. (In issues and PR descriptions relative paths don't resolve — use the next option there.) - Upload through the GitHub editor. Drag-and-drop an image into an issue, PR or the web file editor and GitHub hosts it on a
githubusercontent.comURL you can reference anywhere. - For badges and diagrams, use hosted services. Shields.io badges, hosted chart images or an
mermaidcode block cover most of the reasons people reach for Base64 in the first place.
If you still want a Base64 string for local previews of that same README, generate one in the browser with the Image to Base64 converter — and remember you will need the hosted fallback for GitHub itself.
Notion: the API only accepts hosted URLs
Notion's API is unambiguous: image blocks take an external HTTPS URL (or a file previously uploaded through Notion). There is no field for inline Base64, and passing a data:image/...;base64, string fails. The File Upload API added in 2025 doesn't change this for block content — it still doesn't accept inline data URIs. Automation builders keep hitting this wall: the n8n and Latenode community forums are full of "why can't I push this base64 image into Notion" threads.
Fixes that work in Notion
- In automations: decode, host, then link. Decode the Base64 to a binary file, upload it to any host that returns an HTTPS URL (S3, Cloudinary, a GitHub raw URL), then create the image block with that URL. Tools like the Base64 payload builder show the encode direction; the decode side is a one-line
Buffer.from(str, 'base64')orbase64 -d. - Manual: paste or upload in the app. Notion re-hosts pasted images as attachments, which is why the app works while the API doesn't.
- For markdown imports, use real URLs. Notion's importer fetches and re-hosts remote images, but a data URI inside imported markdown does not survive — convert your images to hosted URLs first.
WordPress: three places data URIs get stripped
WordPress is the subtlest of the three because the behavior depends on who is editing and where the HTML ends up. The symptom is always some flavor of "the image was there in preview and gone after save" — but the culprit differs:
- The
wp_ksessanitizer. Users without theunfiltered_htmlcapability — authors and contributors, and editors on multisite — getdata:removed fromsrcattributes on save. Administrators usually keep their images, which makes the bug look random. If an admin sees the image and an author doesn't, this is it. - Editor round-trips. Switching between visual and code editing, or between the block and classic editors, can mangle very long attribute values. A 50 KB data URI is a long attribute.
- SMTP and email-log plugins. Plugins that send or log transactional HTML often sanitize the message body — your post renders fine in the editor but the Base64 images vanish from the sent email. WordPress.org support threads document exactly this, including in mail-logging contexts.
Fixes that work in WordPress
- Allow the
dataprotocol for KSES — in a plugin or theme functions file:
One caution: this whitelistsadd_filter( 'wp_kses_allowed_protocols', function ( $protocols ) { $protocols[] = 'data'; return $protocols; } );data:everywhere KSES applies, including links.data:text/htmlURLs are a known phishing vector, so on multi-author or open-registration sites prefer the next option. - Use the Media Library instead. Hosting through WordPress's own media system is the platform-native path, avoids the sanitizer entirely, and performs better: large data URIs bloat the HTML of every page view and delay first paint.
- Diagnose by role and destination. Admin sees it, author doesn't →
wp_kses. Editor fine, sent email stripped → the mail plugin. Preview fine, gone after save → editor round-trip.
Where Base64 in markdown works well
The legitimate use case is single-file portability — a markdown document that travels with its images intact: notes emailed as one .md, archived documentation, a self-contained spec that renders in Typora, Obsidian or a pandoc-generated standalone HTML file. In those environments data URIs are genuinely useful.
- Typora renders them natively.
- Obsidian renders them in reading view when the MIME type is exact.
- VS Code preview mostly works — but if one string fails while another renders, you are probably hitting the known parsing bug, not a bad encoding.
Watch the weight: Base64 inflates files by ~33%, and a few screenshots can turn a 20 KB document into a megabyte slug. Compress first (see the PNG and JPG compressors) and check the result with the size calculator. If you inherit a document stuffed with data URIs, the Base64 extractor pulls them back out as files.
Universal pitfalls that break data URIs anywhere
Even on renderers that fully support data URIs, four mistakes account for most "my Base64 image doesn't show" reports:
- Wrong MIME type.
data:image/jpg;base64,is invalid — the registered type isimage/jpeg. Strict renderers (Obsidian, VS Code preview in some versions) refuse the whole URI over this. - Line breaks in the payload. Many CLI encoders wrap Base64 at 76 characters by default, and markdown renderers do not strip those newlines inside the URI. Use
base64 -w 0on Linux (see the per-language conversion guide) or a browser encoder that outputs one line. - Stray whitespace or a BOM picked up during copy-paste — invisible in an editor, fatal to the parser.
- Assuming your editor's renderer is the target. The single most common trap in this whole topic: testing in Typora or VS Code and shipping to GitHub or Notion. Always verify in the renderer that will actually display the file.
For corrupted strings rather than blocked ones — truncation, missing padding, stray characters — the invalid Base64 data guide covers diagnosis step by step.
Frequently asked questions
Why is my Base64 image not showing in GitHub markdown?
GitHub Flavored Markdown explicitly disallows data: URIs, so Base64 images are structurally blocked on github.com in READMEs, issues and discussions. Commit the image and use a relative path, or upload it in the GitHub editor for a hosted URL.
Does Notion support Base64 or data URI images?
Not via the API — image blocks accept external HTTPS URLs only, and the File Upload API doesn't take inline data URIs. Decode and upload to an external host, then pass the URL. Pasting in the app works because Notion re-hosts the file.
Why does my WordPress post lose Base64 images when I save?
Usually wp_kses stripping data: from users without unfiltered_html; editor round-trips and SMTP/email-log plugins are the other two common culprits. Allow the data protocol via wp_kses_allowed_protocols, or host images in the Media Library.
Which markdown editors render Base64 images?
Typora and Obsidian do, and VS Code's preview mostly does (with a known parsing bug on some strings). These local renderers are where single-file Base64 markdown shines.
Why does my Base64 markdown work in Typora but not on GitHub?
Rendering happens where the file is displayed: Typora renders locally with full support, github.com sanitizes data: URIs out. Test in the renderer that will actually display the file.
How do I make a Base64 data URI for markdown?
Encode the image in your browser with the Image to Base64 converter — nothing is uploaded — and copy the data URI output. Keep the source image small: Base64 adds about 33% to file size.
Need a clean, single-line data URI to drop into a markdown file? Encode it locally with the Image to Base64 converter, size it with the calculator, and check the platform matrix above before you ship.