<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Mind vs Drive Tools]]></title><description><![CDATA[Mind vs Drive Tools]]></description><link>https://mindvsdrivetools.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 13 Nov 2026 19:38:36 GMT</lastBuildDate><atom:link href="https://mindvsdrivetools.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Why Base64 Breaks in a Query String (The "+" That Turns Into a Space)]]></title><description><![CDATA[A bug report I see in some form every few weeks: "The Base64 value decodes fine on my machine, but when it comes back through the URL it decodes to…]]></description><link>https://mindvsdrivetools.hashnode.dev/why-base64-breaks-in-a-query-string-the-that-turns-into-a-space</link><guid isPermaLink="true">https://mindvsdrivetools.hashnode.dev/why-base64-breaks-in-a-query-string-the-that-turns-into-a-space</guid><category><![CDATA[Base64]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Python]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Mind vs Drive]]></dc:creator><pubDate>Fri, 13 Nov 2026 08:00:29 GMT</pubDate><content:encoded><![CDATA[<p>A bug report I see in some form every few weeks: "The Base64 value decodes fine on my machine, but when it comes back through the URL it decodes to garbage." Nine times out of ten, nothing is wrong with Base64. The value went through a URL, and one or more of its characters were changed on the way.</p>
<p>This post walks through exactly what happens, with one small worked example you can check yourself, and the two fixes that actually work.</p>
<p><em>Disclosure: I built <a href="https://base64decodetool.com/">base64decodetool.com</a>, including the URL-safe Base64 tool linked below. All values in this post are illustrative; there are no real tokens or credentials anywhere in it.</em></p>
<h2>The three characters that cause the trouble</h2>
<p>Standard Base64 (RFC 4648, section 4) uses 64 characters: <code>A–Z</code>, <code>a–z</code>, <code>0–9</code>, plus <code>+</code> and <code>/</code>, with <code>=</code> as padding at the end. Those last three are the problem, because all of them mean something in a URL:</p>
<ul>
<li><code>+</code> in a query string is read as a <strong>space</strong> by HTML form decoding (<code>application/x-www-form-urlencoded</code>), which is what most server frameworks and <code>URLSearchParams</code> use.</li>
<li><code>/</code> is a path separator. Inside a query value it is usually tolerated, but inside a path segment it splits the path.</li>
<li><code>=</code> separates a parameter's name from its value. Extra <code>=</code> signs at the end of a value are often kept, but some parsers, proxies or logging tools trim or mangle them.</li>
</ul>
<p>So a Base64 string that is perfectly valid on its own can be quietly changed the moment you drop it into <code>?data=...</code> without encoding it.</p>
<h2>A worked example: <code>price&gt;=100?</code></h2>
<p>Take the short, made-up text <code>price&gt;=100?</code> (11 bytes of ASCII).</p>
<p><strong>Step 1: standard Base64.</strong> Encoding it gives:</p>
<pre><code>cHJpY2U+PTEwMD8=
</code></pre>
<p>Notice the <code>+</code> in the middle and the <code>=</code> at the end. Eleven bytes is not a multiple of three, so there is one padding character (11 bytes → 4 × ceil(11/3) = 16 characters).</p>
<p><strong>Step 2: put it in a URL without encoding.</strong></p>
<pre><code>https://example.com/report?d=cHJpY2U+PTEwMD8=
</code></pre>
<p><strong>Step 3: read it back on the server.</strong> With form-style query decoding, for example <code>new URLSearchParams("d=cHJpY2U+PTEwMD8=").get("d")</code> in JavaScript, you get:</p>
<pre><code>cHJpY2U PTEwMD8=
</code></pre>
<p>The <code>+</code> is now a space.</p>
<p><strong>Step 4: decode that.</strong> What happens next depends on the decoder. A strict decoder (Python's <code>base64.b64decode</code> with <code>validate=True</code>, for example) rejects the space and throws an error. That is the lucky case, because you notice. A lenient decoder that throws away anything that is not a Base64 character gives you <code>cHJpY2UPTEwMD8=</code>, which is now the wrong length and the wrong bits. Decoding that produces the bytes <code>70 72 69 63 65 0F 4C 4C 0C 0F</code>, which shows up as <code>price</code> followed by control characters and <code>LL</code>. The first five letters survive, everything after the <code>+</code> is shifted, and nothing tells you why.</p>
<p>I checked those exact bytes with Node's <code>atob</code> after the same cleanup step a lenient decoder does. It is a good illustration of why "it decodes without an error" does not mean "it decoded correctly".</p>
<h2>Fix 1: percent-encode the value</h2>
<p>If the receiving side expects standard Base64, encode the value as a URL component before putting it in the URL:</p>
<pre><code class="language-js">const b64 = "cHJpY2U+PTEwMD8=";
const url = "https://example.com/report?d=" + encodeURIComponent(b64);
// ...?d=cHJpY2U%2BPTEwMD8%3D
</code></pre>
<p><code>%2B</code> is a literal plus and <code>%3D</code> is a literal equals sign, so the server's query parser gives you back exactly <code>cHJpY2U+PTEwMD8=</code>. Most HTTP client libraries do this for you when you pass parameters as an object instead of building the string by hand, which is another good reason to stop concatenating URLs.</p>
<h2>Fix 2: use Base64URL from the start</h2>
<p>RFC 4648 section 5 defines a URL- and filename-safe alphabet that swaps the two problem characters:</p>
<table>
<thead>
<tr>
<th>Standard</th>
<th>Base64URL</th>
</tr>
</thead>
<tbody><tr>
<td><code>+</code></td>
<td><code>-</code></td>
</tr>
<tr>
<td><code>/</code></td>
<td><code>_</code></td>
</tr>
<tr>
<td><code>=</code> padding</td>
<td>usually omitted</td>
</tr>
</tbody></table>
<p>The same text, <code>price&gt;=100?</code>, becomes:</p>
<pre><code>cHJpY2U-PTEwMD8
</code></pre>
<p>Nothing in that string needs escaping in a query value or a path segment. This is why JWTs, many signed-URL schemes and lots of API tokens use Base64URL rather than plain Base64.</p>
<p>In code:</p>
<pre><code class="language-js">// Node.js 16+
Buffer.from("price&gt;=100?").toString("base64url"); // "cHJpY2U-PTEwMD8"
</code></pre>
<pre><code class="language-python">import base64
base64.urlsafe_b64encode(b"price&gt;=100?").rstrip(b"=")  # b'cHJpY2U-PTEwMD8'
</code></pre>
<p>When you decode, convert <code>-</code> back to <code>+</code> and <code>_</code> back to <code>/</code>, then add <code>=</code> until the length is a multiple of 4. Python's <code>urlsafe_b64decode</code> does the character swap but still needs the padding put back, which trips people up.</p>
<h2>Which fix should you pick?</h2>
<ul>
<li><strong>You control both ends:</strong> use Base64URL. It is shorter (no padding), it survives URLs, cookies and filenames untouched, and you never have to remember to escape.</li>
<li><strong>The other side insists on standard Base64:</strong> keep it, but always percent-encode it as a URL component, and let your HTTP library build the query string.</li>
<li><strong>You are debugging a value that already went through a URL:</strong> look for spaces where a <code>+</code> should be. Replacing spaces with <code>+</code> before decoding can rescue the data, but treat that as a patch, not a design.</li>
</ul>
<h2>A quick checklist when a Base64 value "decodes to garbage"</h2>
<ol>
<li>Did it pass through a URL, a form post or a cookie? Check for spaces (lost <code>+</code>), missing <code>=</code> or <code>%2B</code>/<code>%2F</code> left undecoded.</li>
<li>Is the length a multiple of 4 after you add padding back? If it is 1 more than a multiple of 4, characters were lost.</li>
<li>Is it actually Base64URL (contains <code>-</code> or <code>_</code>)? Decode with the URL-safe alphabet.</li>
<li>Does the output start correctly and then go wrong? The break point is usually right where a <code>+</code> or <code>/</code> used to be.</li>
</ol>
<h2>How to check it with the tool</h2>
<ol>
<li>Open the <a href="https://base64decodetool.com/url-safe/">URL-safe Base64 encoder and decoder</a>. This page always works in the URL-safe format.</li>
<li>Choose <strong>Encode to Base64</strong>, type your text (for example <code>price&gt;=100?</code>) and press <strong>Convert</strong>. You should see <code>cHJpY2U-PTEwMD8</code>.</li>
<li>Switch to <strong>Decode Base64</strong>, paste a value and press <strong>Convert</strong>. It accepts both alphabets and adds missing padding, so <code>cHJpY2U-PTEwMD8</code> and <code>cHJpY2U+PTEwMD8=</code> both give <code>price&gt;=100?</code>.</li>
</ol>
<p>One honest caveat: like many lenient decoders, the tool quietly drops characters that are not part of either Base64 alphabet, including spaces. If you paste <code>cHJpY2U PTEwMD8=</code> it will not warn you about the missing <code>+</code>; it will show the broken result from the example above. If the output looks wrong, compare the input against the checklist first.</p>
<p>Everything runs in the browser, but as with any tool, don't paste real secrets or production tokens.</p>
]]></content:encoded></item><item><title><![CDATA[Hex to Base64 (and Back): One Set of Bytes, Two Ways to Write It]]></title><description><![CDATA[Hex and Base64 are two ways of writing the same raw bytes as plain text. Neither one encrypts, compresses or hides anything. They just turn up in different…]]></description><link>https://mindvsdrivetools.hashnode.dev/hex-to-base64-and-back-one-set-of-bytes-two-ways-to-write-it</link><guid isPermaLink="true">https://mindvsdrivetools.hashnode.dev/hex-to-base64-and-back-one-set-of-bytes-two-ways-to-write-it</guid><category><![CDATA[Base64]]></category><category><![CDATA[encoding]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Mind vs Drive]]></dc:creator><pubDate>Thu, 12 Nov 2026 07:54:07 GMT</pubDate><content:encoded><![CDATA[<p>Hex and Base64 are two ways of writing the same raw bytes as plain text. Neither one encrypts, compresses or hides anything. They just turn up in different places. Hex is common in packet captures, hash outputs, colour codes, debugger memory views and <code>0x…</code> literals. Base64 is common in JSON payloads, email attachments, data URIs, JWTs and Subresource Integrity (SRI) attributes.</p>
<p>So when a hash utility prints hex and your config file wants Base64, or a log gives you Base64 and you want to look at the bytes, you have to convert. Below I explain how the conversion works, walk through an example by hand, list the mistakes that cause most failed conversions, and finish with a short guide to the free converter I built.</p>
<h2>Same bytes, two alphabets</h2>
<p>Take the five ASCII bytes of the sample word <code>Hello</code>:</p>
<ul>
<li><strong>Hex:</strong> <code>48656c6c6f</code>. Each byte is two characters from <code>0-9a-f</code>.</li>
<li><strong>Base64:</strong> <code>SGVsbG8=</code>. Every 3 bytes become 4 characters from <code>A-Z a-z 0-9 + /</code>, and <code>=</code> pads the end when the byte count isn't a multiple of 3.</li>
</ul>
<p>Both strings describe exactly the same bytes: <code>0x48 0x65 0x6c 0x6c 0x6f</code>. Converting from one to the other is a two-step job. First decode the text back into bytes, then encode those bytes in the other format. You never need to go through "text" or a character set, which is why the conversion is lossless for any binary data, not only ASCII.</p>
<h2>Worked example: converting by hand</h2>
<p>Here is the first chunk, the illustrative hex input <code>48656c</code>, worked out step by step.</p>
<p><strong>Step 1: hex to bytes.</strong> Split the input into pairs: <code>48</code>, <code>65</code>, <code>6c</code>. That gives three bytes.</p>
<p><strong>Step 2: bytes to bits.</strong></p>
<pre><code>48 → 01001000
65 → 01100101
6c → 01101100
</code></pre>
<p>Joined together: <code>010010000110010101101100</code> (24 bits).</p>
<p><strong>Step 3: regroup into 6-bit chunks.</strong> Base64 uses 6 bits per character, because 2^6 = 64:</p>
<pre><code>010010 | 000110 | 010101 | 101100
  18   |   6    |   21   |   44
</code></pre>
<p><strong>Step 4: look up the Base64 alphabet.</strong> Indexes 0–25 are <code>A–Z</code>, 26–51 are <code>a–z</code>, 52–61 are <code>0–9</code>, then <code>+</code> and <code>/</code>.</p>
<ul>
<li>18 → <code>S</code></li>
<li>6 → <code>G</code></li>
<li>21 → <code>V</code></li>
<li>44 → <code>s</code> (44 − 26 = 18, the 19th lowercase letter)</li>
</ul>
<p><strong>Result:</strong> <code>48656c</code> → <code>SGVs</code>. For the full <code>48656c6c6f</code>, the last two bytes (<code>6c 6f</code>) are only 16 bits. They get zero-padded to 18 bits, which gives three characters (<code>bG8</code>), and one <code>=</code> brings the length up to a multiple of 4. The whole answer is <code>SGVsbG8=</code>.</p>
<p><strong>Check:</strong> I ran both directions in Python and through the same JavaScript logic the converter page uses:</p>
<pre><code class="language-python">import base64, binascii
print(base64.b64encode(bytes.fromhex("48656c6c6f")).decode())  # SGVsbG8=
print(binascii.hexlify(base64.b64decode("SGVsbG8=")).decode())  # 48656c6c6f
</code></pre>
<h2>A practical example: a SHA-256 hash for an SRI attribute</h2>
<p>Command-line hash tools usually print hex. Subresource Integrity attributes (<code>integrity="sha256-..."</code>) expect the same digest in Base64. A safe public value to practise on is the SHA-256 of an <strong>empty input</strong>. It's a well-known constant, not a secret:</p>
<ul>
<li>Hex (64 characters):
<code>e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855</code></li>
<li>Base64 (44 characters):
<code>47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=</code></li>
</ul>
<p>The lengths show the size trade-off. A 32-byte digest takes 64 hex characters (2 per byte) but only 44 Base64 characters (4 per 3 bytes, rounded up). That's about 31% shorter, which is why APIs and HTML attributes tend to prefer Base64.</p>
<p>A common mistake is Base64-encoding the <strong>hex text</strong> instead of the <strong>bytes</strong>. Encode the 64-character string <code>e3b0c4...</code> as text and you get a much longer value, and it won't match anything a browser computes.</p>
<h2>A sniff test: recognising file types</h2>
<p>Hex-to-Base64 conversion also helps you spot what a mystery blob is. Every PNG starts with the public 8-byte signature <code>89504e470d0a1a0a</code>, which in Base64 is <code>iVBORw0KGgo=</code>. So if a log line or JSON field begins with <code>iVBORw0KGgo</code>, it's almost certainly a PNG and not ciphertext. Likewise, the JPEG start bytes <code>ffd8ffe0</code> become <code>/9j/4A==</code>, so Base64 that begins with <code>/9j/</code> is usually a JPEG.</p>
<h2>Mistakes that cause most failed conversions</h2>
<ul>
<li><strong><code>0x</code> prefixes.</strong> <code>0x48 0x65</code> is a common way to write two bytes, but the <code>0</code> in each <code>0x</code> is a valid hex digit. Many tools (including mine, see below) strip the <code>x</code> but keep the <code>0</code>, so you either get an "odd length" error or silently wrong bytes. Remove every <code>0x</code> before converting.</li>
<li><strong>Odd number of hex digits.</strong> Every byte needs exactly two digits, so <code>abc</code> is not a complete byte string. Find the missing leading zero (often <code>0a</code> written as <code>a</code>).</li>
<li><strong>URL-safe Base64.</strong> Base64URL replaces <code>+</code> and <code>/</code> with <code>-</code> and <code>_</code> and often drops the <code>=</code> padding. That's what you'll find in JWTs and many web tokens. A strict decoder may reject it, so map <code>-</code>→<code>+</code> and <code>_</code>→<code>/</code> and pad to a multiple of 4.</li>
<li><strong>Pasting a whole data URI.</strong> <code>data:image/png;base64,</code> is a label, not data. Convert only what comes after the comma.</li>
<li><strong>Mixing up the modes.</strong> A string like <code>eyJhbGciOi...</code> is Base64URL (a JWT header). It is not hex, because hex contains only <code>0-9</code> and <code>a-f</code>/<code>A-F</code>.</li>
</ul>
<h2>How to use the converter</h2>
<p>Open <a href="https://base64decodetool.com/hex/">https://base64decodetool.com/hex/</a></p>
<ol>
<li>Click <strong>Hex to Base64</strong> or <strong>Base64 to Hex</strong> at the top of the tool.</li>
<li>Paste into the input box. Its label changes to <strong>Hex String Input</strong> or <strong>Base64 Input</strong>, and the result appears straight away under <strong>Base64 Output</strong> or <strong>Hexadecimal Output</strong>. <strong>Load Sample</strong> fills in <code>48656c6c6f20576f726c64</code> ("Hello World", output <code>SGVsbG8gV29ybGQ=</code>) if you want to test it first.</li>
<li>Click <strong>Copy</strong> to take the result. <strong>Swap Inputs</strong> moves the output into the input and flips the direction, which is a quick way to check a round trip. <strong>Clear</strong> empties the box.</li>
</ol>
<p>A few honest notes on how the page behaves (I checked its JavaScript today):</p>
<ul>
<li>In Hex to Base64 mode it <strong>removes any character that isn't hex</strong> before converting. Spaces, colons and line breaks in pasted dumps are fine (<code>89 50 4E 47 0D 0A 1A 0A</code> still gives <code>iVBORw0KGgo=</code>). The catch is that a stray typo gets dropped without a warning instead of raising an error, and <code>0x</code> prefixes cause the problem described above. Clean up your input first.</li>
<li>In Base64 to Hex mode it accepts URL-safe characters and missing padding (<code>3q2-7w</code> gives <code>DE AD BE EF</code>). The hex output is <strong>uppercase and space-separated</strong>, so remove the spaces if your next tool wants one continuous string.</li>
<li>If you paste a full data URI that has <code>=</code> padding into Base64 to Hex, it currently shows an error. Paste only the part after <code>base64,</code>.</li>
</ul>
<h2>Encoding is not encryption</h2>
<p>Anyone can reverse hex or Base64 in one step. Use them to move bytes through text-only channels. For secrecy use real encryption, and for stored passwords use a proper password hash, not a reversible encoding.</p>
<h2>Disclosure</h2>
<p>I built the free Hex ↔ Base64 converter at <a href="https://base64decodetool.com/hex/">https://base64decodetool.com/hex/</a>. All values in this post are illustrative samples ("Hello", the public PNG/JPEG signatures and the SHA-256 of empty input). The converter runs in your browser, but as a habit, don't paste live production secrets into any online tool.</p>
]]></content:encoded></item><item><title><![CDATA[Why btoa() Fails on Emoji and Hindi (and Quietly Gets "café" Wrong): Base64-Encoding Unicode Text the Right Way]]></title><description><![CDATA[Disclosure: I built the free Base64 Encoder on base64decodetool.com that I use for checks in this post. If you've ever called btoa() on user input in the…]]></description><link>https://mindvsdrivetools.hashnode.dev/why-btoa-fails-on-emoji-and-hindi-and-quietly-gets-caf-wrong-base64-encoding-unicode-text-the-right-way</link><guid isPermaLink="true">https://mindvsdrivetools.hashnode.dev/why-btoa-fails-on-emoji-and-hindi-and-quietly-gets-caf-wrong-base64-encoding-unicode-text-the-right-way</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Base64]]></category><category><![CDATA[unicode]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Mind vs Drive]]></dc:creator><pubDate>Wed, 11 Nov 2026 08:03:57 GMT</pubDate><content:encoded><![CDATA[<p><em>Disclosure: I built the free <a href="https://base64decodetool.com/encode/">Base64 Encoder</a> on base64decodetool.com that I use for checks in this post.</em></p>
<p>If you've ever called <code>btoa()</code> on user input in the browser, you've probably seen this:</p>
<pre><code>Uncaught InvalidCharacterError: Failed to execute 'btoa' on 'Window':
The string to be encoded contains characters outside of the Latin1 range.
</code></pre>
<p>That message is from Chrome. Other browsers word it differently, but it's the same <code>InvalidCharacterError</code> <code>DOMException</code>. It shows up as soon as someone types an emoji, a Hindi name, or a Chinese address.</p>
<p>The quick fix you find online is a snippet built on the long-deprecated <code>unescape(encodeURIComponent(...))</code> trick. A better fix is understanding <em>why</em> it happens. Base64 encodes <strong>bytes</strong>, not characters, so you have to decide how your text becomes bytes before you encode it.</p>
<h2>What btoa() actually expects</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa">MDN's btoa page</a> puts it plainly: the input is a "binary string", and every character must have a code point below 256, because each character is treated as one byte. JavaScript strings are UTF-16, so anything above U+00FF can't fit in a byte, and <code>btoa()</code> throws.</p>
<p>There's a nasty middle case, though. Accented Latin letters like <code>é</code> (U+00E9) <strong>are</strong> below 256, so <code>btoa("café")</code> doesn't throw. It quietly encodes <code>é</code> as the single byte <code>0xE9</code>, which is Latin-1, not UTF-8:</p>
<pre><code class="language-js">btoa("café");   // "Y2Fm6Q=="  (Latin-1 bytes, no error)
</code></pre>
<p>Hand that to a Python service or a Go API that decodes as UTF-8 and you get <code>caf�</code>. No error is raised anywhere. You just get a corrupted name in the database.</p>
<h2>The fix: turn text into UTF-8 bytes first</h2>
<p>UTF-8 is what almost every API, JSON parser and database expects, so encode to UTF-8 first and Base64 second.</p>
<p><strong>Browser (and Deno/Node), using <a href="https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder">TextEncoder</a>:</strong></p>
<pre><code class="language-js">function toBase64Utf8(text) {
  const bytes = new TextEncoder().encode(text);   // UTF-8 bytes
  let binary = "";
  for (const b of bytes) binary += String.fromCharCode(b);
  return btoa(binary);                            // now every char is &lt; 256
}
</code></pre>
<p>For very large inputs, build the binary string in chunks rather than one character at a time. Newer runtimes also have <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64"><code>Uint8Array.prototype.toBase64()</code></a>, which MDN marks as Baseline 2025. Check that your targets support it before you rely on it.</p>
<p><strong>Node.js:</strong></p>
<pre><code class="language-js">Buffer.from("Hi 👋", "utf8").toString("base64");   // "SGkg8J+Riw=="
</code></pre>
<p><strong>Python:</strong></p>
<pre><code class="language-python">import base64
base64.b64encode("Hi 👋".encode("utf-8")).decode("ascii")   # 'SGkg8J+Riw=='
</code></pre>
<h2>Worked examples: three illustrative strings</h2>
<p>I encoded three made-up sample strings as UTF-8 and checked the results three ways: the encoder's own JavaScript run in Node, Node's <code>Buffer</code>, and Python's <code>base64</code> module. All three matched.</p>
<table>
<thead>
<tr>
<th>Input (illustrative)</th>
<th>JS <code>.length</code></th>
<th>UTF-8 bytes</th>
<th>Base64 (UTF-8)</th>
<th>Base64 length</th>
</tr>
</thead>
<tbody><tr>
<td><code>café</code></td>
<td>4</td>
<td>5</td>
<td><code>Y2Fmw6k=</code></td>
<td>8</td>
</tr>
<tr>
<td><code>Hi 👋</code></td>
<td>5</td>
<td>7</td>
<td><code>SGkg8J+Riw==</code></td>
<td>12</td>
</tr>
<tr>
<td><code>नमस्ते</code></td>
<td>6</td>
<td>18</td>
<td><code>4KSo4KSu4KS44KWN4KSk4KWH</code></td>
<td>24</td>
</tr>
</tbody></table>
<p>A few things stand out:</p>
<ul>
<li><strong><code>é</code> takes 2 bytes</strong> in UTF-8 (<code>C3 A9</code>), which is why <code>café</code> gives <code>Y2Fmw6k=</code> and not the Latin-1 <code>Y2Fm6Q==</code>.</li>
<li><strong><code>👋</code> is 4 bytes</strong> (<code>F0 9F 91 8B</code>). JavaScript reports <code>"Hi 👋".length</code> as 5 because the emoji is a surrogate pair, while Python's <code>len()</code> says 4. Neither number is the byte count.</li>
<li><strong>Each Devanagari letter or sign is 3 bytes.</strong> <code>नमस्ते</code> is 6 code points (and only 3 visible clusters on screen), but 18 bytes.</li>
</ul>
<h2>The size math: count bytes, not characters</h2>
<p>Base64 (<a href="https://www.rfc-editor.org/rfc/rfc4648.html">RFC 4648</a>, section 4) turns every 3 bytes into 4 characters and pads the end with <code>=</code>:</p>
<pre><code>base64_length = 4 × ceil(utf8_bytes / 3)
</code></pre>
<ul>
<li><code>café</code>: 4 × ceil(5/3) = 4 × 2 = <strong>8</strong>. 5 bytes is one full group plus 2 left over, which gives one <code>=</code>.</li>
<li><code>Hi 👋</code>: 4 × ceil(7/3) = 4 × 3 = <strong>12</strong>. 1 byte left over gives <code>==</code>.</li>
<li><code>नमस्ते</code>: 4 × ceil(18/3) = 4 × 6 = <strong>24</strong>. 18 is a multiple of 3, so no padding.</li>
</ul>
<p>This matters when a field has a length limit. A 100-character Hindi name can be around 300 UTF-8 bytes, which becomes about 400 Base64 characters. If you size the column from the character count, you'll truncate it.</p>
<h2>Same text, different bytes, different Base64</h2>
<p>There's no single "Base64 of a string" until you choose a character encoding. Here's the same input in other encodings (checked with Python):</p>
<table>
<thead>
<tr>
<th>Input</th>
<th>UTF-8</th>
<th>UTF-16LE</th>
<th>Latin-1</th>
</tr>
</thead>
<tbody><tr>
<td><code>café</code></td>
<td><code>Y2Fmw6k=</code></td>
<td><code>YwBhAGYA6QA=</code></td>
<td><code>Y2Fm6Q==</code></td>
</tr>
<tr>
<td><code>Hi 👋</code></td>
<td><code>SGkg8J+Riw==</code></td>
<td><code>SABpACAAPdhL3A==</code></td>
<td>can't be encoded</td>
</tr>
<tr>
<td><code>नमस्ते</code></td>
<td><code>4KSo4KSu4KS44KWN4KSk4KWH</code></td>
<td><code>KAkuCTgJTQkkCUcJ</code></td>
<td>can't be encoded</td>
</tr>
</tbody></table>
<p>UTF-16LE is what you'll run into with Windows APIs and PowerShell's <code>-EncodedCommand</code>. Notice that <code>नमस्ते</code> is actually <em>shorter</em> in UTF-16 (12 bytes) than in UTF-8 (18 bytes). If two systems produce different Base64 for "the same" text, a mismatched encoding is the first thing I check.</p>
<h2>Decoding back without mojibake</h2>
<p><code>atob()</code> has the mirror-image problem. It returns a binary string, one character per byte, so <code>atob("Y2Fmw6k=")</code> gives <code>cafÃ©</code>. You need to rebuild the bytes and decode them as UTF-8:</p>
<pre><code class="language-js">function fromBase64Utf8(b64) {
  const binary = atob(b64);
  const bytes = Uint8Array.from(binary, (ch) =&gt; ch.charCodeAt(0));
  return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
fromBase64Utf8("4KSo4KSu4KS44KWN4KSk4KWH");   // "नमस्ते"
</code></pre>
<p><code>{ fatal: true }</code> makes <a href="https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder">TextDecoder</a> throw a <code>TypeError</code> on bytes that aren't valid UTF-8. Without it, you get a silent <code>�</code>. Feeding it the Latin-1 <code>Y2Fm6Q==</code> throws, which is the result you want. In Python, use <code>base64.b64decode(s).decode("utf-8")</code>, and in Node, <code>Buffer.from(s, "base64").toString("utf8")</code>.</p>
<h2>How to use the encoder</h2>
<ol>
<li>Open <a href="https://base64decodetool.com/encode/">base64decodetool.com/encode/</a>. Make sure <strong>Encode to Base64</strong> is selected and <strong>Character Set</strong> is <strong>UTF-8</strong>.</li>
<li>Type or paste into <strong>Input String</strong>. With <strong>Live Conversion</strong> on, the Base64 appears straight away. The counter under the input shows characters and bytes (for <code>Hi 👋</code> it reads 5 chars / 7 B).</li>
<li>Turn on <strong>URL-Safe Format</strong> if the value is going into a URL or JWT-style token (<code>Hi 👋</code> becomes <code>SGkg8J-Riw</code>). Then click <strong>Copy</strong>, or <strong>Swap Inputs</strong> to decode it back.</li>
</ol>
<p>The encoding runs in your browser. Still, test with sample text like the strings above rather than real customer data.</p>
]]></content:encoded></item><item><title><![CDATA[Convertir una imagen a Base64 y usarla como data URI en CSS]]></title><description><![CDATA[Un ejemplo real paso a paso: de un PNG de 75 bytes a background-image, con cálculo del tamaño y cuándo conviene (y cuándo no)]]></description><link>https://mindvsdrivetools.hashnode.dev/imagen-base64-data-uri-css</link><guid isPermaLink="true">https://mindvsdrivetools.hashnode.dev/imagen-base64-data-uri-css</guid><category><![CDATA[CSS]]></category><category><![CDATA[Base64]]></category><category><![CDATA[HTML]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Mind vs Drive]]></dc:creator><pubDate>Tue, 10 Nov 2026 08:03:14 GMT</pubDate><content:encoded><![CDATA[<p>Incrustar una imagen pequeña directamente en el CSS o en el HTML ahorra una petición al servidor y evita que un icono "parpadee" mientras carga. La técnica se llama <strong>data URI</strong>: en lugar de una ruta como <code>icono.png</code>, escribes la imagen codificada en Base64 dentro de la propia hoja de estilos.</p>
<p>En este artículo vamos a hacerlo con un ejemplo real y comprobable: crearemos una imagen diminuta, la convertiremos a Base64, la usaremos en CSS y en HTML, y verificaremos que al decodificarla obtenemos exactamente los mismos bytes.</p>
<blockquote>
<p><strong>Divulgación:</strong> la herramienta que enlazo, <a href="https://base64decodetool.com/es/image/">Convertir Imagen a Base64</a>, la creó Nagesh, el autor de este blog.</p>
</blockquote>
<h2>Qué es un data URI</h2>
<p>Un data URI tiene esta forma:</p>
<pre><code>data:[tipo MIME];base64,[datos codificados]
</code></pre>
<p>Para un PNG sería <code>data:image/png;base64,</code> seguido de la cadena Base64. El navegador lo trata como si fuera un archivo: puedes ponerlo en <code>src</code> de una etiqueta <code>&lt;img&gt;</code> o dentro de <code>url()</code> en CSS.</p>
<h2>Ejemplo práctico: un PNG de 2×2 píxeles</h2>
<p><em>Los datos de este ejemplo son ilustrativos: la imagen la generé yo mismo para el artículo, no proviene de ningún proyecto real.</em></p>
<p>Creé con Python (sin librerías externas, solo <code>zlib</code> y <code>struct</code>) un PNG de 2×2 píxeles con un patrón de tablero: rojo <code>(255, 0, 0)</code> y azul <code>(0, 128, 255)</code> alternados. El archivo resultante, <code>punto.png</code>, ocupa <strong>75 bytes</strong>.</p>
<p>Para codificarlo, basta con la biblioteca estándar:</p>
<pre><code class="language-python">import base64

with open("punto.png", "rb") as f:
    datos = f.read()

b64 = base64.b64encode(datos).decode("ascii")
print(len(datos), len(b64))   # 75 100
print("data:image/png;base64," + b64)
</code></pre>
<p>La salida Base64 real es (100 caracteres):</p>
<pre><code>iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEklEQVR42mP4z8DA0PAfhIEEACLyBP2KEV+xAAAAAElFTkSuQmCC
</code></pre>
<p>Y el data URI completo (122 caracteres):</p>
<pre><code>data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEklEQVR42mP4z8DA0PAfhIEEACLyBP2KEV+xAAAAAElFTkSuQmCC
</code></pre>
<p>Fíjate en que todos los PNG empiezan por <code>iVBORw0KGgo</code>: es la firma binaria del formato (<code>89 50 4E 47 0D 0A 1A 0A</code>) vista en Base64. Si una cadena que dice ser PNG no empieza así, algo está mal.</p>
<h3>Usarlo en CSS</h3>
<pre><code class="language-css">.patron {
  width: 40px;
  height: 40px;
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEklEQVR42mP4z8DA0PAfhIEEACLyBP2KEV+xAAAAAElFTkSuQmCC");
  background-size: 8px 8px;
  image-rendering: pixelated; /* para que se vean los píxeles nítidos al ampliar */
}
</code></pre>
<p>Con <code>background-size</code> y la repetición por defecto, el PNG de 2×2 se convierte en un fondo de tablero rojo y azul.</p>
<h3>Usarlo en HTML</h3>
<pre><code class="language-html">&lt;img
  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAEklEQVR42mP4z8DA0PAfhIEEACLyBP2KEV+xAAAAAElFTkSuQmCC"
  width="40" height="40"
  alt="Patrón de tablero rojo y azul"
  style="image-rendering: pixelated"&gt;
</code></pre>
<p>Escribe siempre un <code>alt</code> descriptivo: que la imagen esté incrustada no cambia nada en accesibilidad.</p>
<h2>Comprobación: ida y vuelta</h2>
<p>Una codificación correcta debe ser reversible sin pérdidas:</p>
<pre><code class="language-python">assert base64.b64decode(b64) == datos   # True
</code></pre>
<p>En mi prueba, decodificar la cadena devolvió exactamente los mismos 75 bytes (huella SHA-256 <code>34391be6…77ba00</code> en ambos casos). Además descargué el JavaScript público de la página de la herramienta y ejecuté con Node.js sus funciones de codificación y decodificación sobre el mismo archivo: produjeron la misma cadena de 100 caracteres que Python, y la decodificación devolvió los 75 bytes idénticos. Para la lectura del archivo, la página usa el <code>FileReader.readAsDataURL()</code> estándar del navegador, que genera el mismo formato <code>data:image/png;base64,…</code>.</p>
<h2>El coste: aproximadamente un 33 % más</h2>
<p>Base64 representa cada 3 bytes con 4 caracteres, así que el tamaño crece en un factor de <strong>4/3</strong> (más el relleno <code>=</code> cuando la longitud no es múltiplo de 3):</p>
<table>
<thead>
<tr>
<th>Concepto</th>
<th>Tamaño</th>
</tr>
</thead>
<tbody><tr>
<td>PNG original</td>
<td>75 bytes</td>
</tr>
<tr>
<td>Base64</td>
<td>100 caracteres (75 ÷ 3 × 4 = 100, sin relleno)</td>
</tr>
<tr>
<td>Prefijo <code>data:image/png;base64,</code></td>
<td>22 caracteres</td>
</tr>
<tr>
<td>Data URI completo</td>
<td>122 caracteres</td>
</tr>
</tbody></table>
<p>En nuestro ejemplo el aumento es exactamente del 33,3 %; con el prefijo, el texto final ocupa un 63 % más que el archivo. En un icono de 75 bytes da igual, pero en una foto de 300 KB serían unos 400 KB de texto (cálculo ilustrativo) metidos dentro de tu CSS. La compresión gzip o Brotli del servidor reduce parte de esa diferencia, pero no toda.</p>
<h2>Base64 no es cifrado</h2>
<p>Base64 es solo una <strong>codificación</strong>: cualquiera puede revertirla en un segundo, sin clave. No sirve para ocultar ni proteger una imagen. Si incrustas algo en tu CSS, es tan público como un archivo enlazado.</p>
<h2>¿Cuándo usar data URIs y cuándo no?</h2>
<p><strong>Úsalos para:</strong></p>
<ul>
<li>Iconos y patrones muy pequeños (unos pocos KB como máximo).</li>
<li>Imágenes críticas para el primer pintado que no quieres que dependan de otra petición.</li>
<li>Correos HTML, prototipos o archivos HTML autocontenidos.</li>
</ul>
<p><strong>Evítalos para:</strong></p>
<ul>
<li>Fotos o imágenes grandes: inflan el CSS/HTML y retrasan el renderizado de toda la página.</li>
<li>Imágenes que se reutilizan en varias páginas: un archivo aparte se guarda en la <strong>caché</strong> del navegador una sola vez; un data URI se vuelve a descargar cada vez que cambia el CSS o el HTML que lo contiene.</li>
<li>Iconos SVG: muchas veces es mejor incrustar el SVG como texto (codificado para URL) que en Base64.</li>
</ul>
<p><strong>Nota sobre CSP:</strong> si tu sitio usa <em>Content Security Policy</em>, las imágenes con <code>data:</code> solo se cargarán si la directiva lo permite, por ejemplo <code>img-src 'self' data:</code>. Si un icono incrustado no aparece, revisa la consola del navegador antes de culpar a la cadena Base64.</p>
<h2>Cómo usar la herramienta</h2>
<p>La página <a href="https://base64decodetool.com/es/image/">base64decodetool.com/es/image/</a> está en español, pero algunos botones aparecen en inglés (comprobado el 10 de noviembre de 2026):</p>
<ol>
<li>Pulsa <strong>Select Image</strong> o arrastra el archivo a la zona "Arrastre y suelte la imagen aquí o haga clic para buscar" (admite PNG, JPG, WEBP, GIF y SVG de hasta 50 MB; todo se procesa en el navegador).</li>
<li>Elige el formato de salida en las pestañas <strong>DataURL</strong>, <strong>HTML</strong>, <strong>CSS</strong> o <strong>Raw</strong> (la pestaña CSS genera la línea <code>background-image: url("…");</code>).</li>
<li>Pulsa <strong>Copiar</strong> y pega el resultado en tu código. Si usas la pestaña HTML, cambia el texto <code>alt</code> genérico por uno descriptivo.</li>
<li>Para el proceso inverso, pega la cadena en <strong>Paste Image Base64 String</strong>, pulsa <strong>Decode &amp; Preview</strong> y luego <strong>Download Image</strong>.</li>
</ol>
<h2>Resumen</h2>
<p>Un data URI es simplemente el archivo en Base64 con el prefijo <code>data:tipo/mime;base64,</code>. Es ideal para iconos diminutos, cuesta alrededor de un 33 % más de tamaño, no protege nada y conviene comprobarlo siempre con una decodificación de ida y vuelta. Con imágenes pequeñas, la ganancia de una petición menos compensa; con imágenes grandes, casi nunca.</p>
]]></content:encoded></item><item><title><![CDATA[Bedanya Base64 dan Base64URL: encode aman untuk URL dan segmen JWT]]></title><description><![CDATA[Pengungkapan: saya Nagesh Subhashchandra, pembuat base64decodetool.com. Alat yang disebut di artikel ini saya bangun sendiri. Semua contoh di sini…]]></description><link>https://mindvsdrivetools.hashnode.dev/bedanya-base64-dan-base64url-encode-aman-untuk-url-dan-segmen-jwt</link><guid isPermaLink="true">https://mindvsdrivetools.hashnode.dev/bedanya-base64-dan-base64url-encode-aman-untuk-url-dan-segmen-jwt</guid><category><![CDATA[Base64]]></category><category><![CDATA[JWT]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Mind vs Drive]]></dc:creator><pubDate>Mon, 09 Nov 2026 11:02:39 GMT</pubDate><content:encoded><![CDATA[<p><em>Pengungkapan: saya Nagesh Subhashchandra, pembuat <a href="https://base64decodetool.com/">base64decodetool.com</a>. Alat yang disebut di artikel ini saya bangun sendiri. Semua contoh di sini sintetik; jangan menempel JWT, kunci API, atau rahasia asli ke alat online mana pun.</em></p>
<p>Base64 sering dipakai untuk membawa data biner lewat saluran teks: lampiran email, data URI, atau JSON. Masalahnya, alfabet standar memakai <code>+</code> dan <code>/</code>, ditambah padding <code>=</code> di akhir. Ketiga karakter itu punya arti khusus di URL: <code>+</code> bisa dibaca sebagai spasi di query string, <code>/</code> memisahkan path, dan <code>=</code> memisahkan nama dan nilai parameter. Karena itu ada <strong>Base64URL</strong> (RFC 4648 bagian 5): alfabetnya sama, tetapi <code>+</code> diganti <code>-</code>, <code>/</code> diganti <code>_</code>, dan padding biasanya dibuang.</p>
<p>Artikel ini membedakan keduanya dengan contoh yang bisa Anda ulang sendiri, lalu menunjukkan cara mencobanya di <a href="https://base64decodetool.com/id/url-safe/">versi Indonesia alat URL-safe</a>.</p>
<h2>Apa saja yang berubah</h2>
<table>
<thead>
<tr>
<th></th>
<th>Base64 standar</th>
<th>Base64URL</th>
</tr>
</thead>
<tbody><tr>
<td>Karakter indeks 62</td>
<td><code>+</code></td>
<td><code>-</code></td>
</tr>
<tr>
<td>Karakter indeks 63</td>
<td><code>/</code></td>
<td><code>_</code></td>
</tr>
<tr>
<td>Padding <code>=</code></td>
<td>Biasanya ada</td>
<td>Biasanya dihilangkan</td>
</tr>
<tr>
<td>Aman ditaruh mentah di URL</td>
<td>Tidak</td>
<td>Ya</td>
</tr>
<tr>
<td>Dipakai di segmen JWT</td>
<td>Tidak</td>
<td>Ya</td>
</tr>
</tbody></table>
<p>Penting: keduanya adalah <strong>pengodean</strong>, bukan enkripsi. Siapa pun yang memegang string bisa mendekodenya. Mendekode JWT juga bukan validasi: memeriksa token tetap butuh verifikasi tanda tangan, <code>alg</code>, <code>exp</code>, <code>iss</code>, dan klaim lain di sisi server.</p>
<h2>Contoh 1: teks pendek "Halo"</h2>
<p>Teks <code>Halo</code> dalam UTF-8 adalah 4 byte. Hasil Base64 standar:</p>
<pre><code>SGFsbw==
</code></pre>
<p>Versi Base64URL:</p>
<pre><code>SGFsbw
</code></pre>
<p>Tidak ada <code>+</code> atau <code>/</code> di sini, jadi bedanya hanya padding. Hitungannya: 4 byte = 32 bit. Base64 mengelompokkan per 6 bit, jadi butuh 6 karakter data, lalu standar menambah <code>==</code> agar panjangnya kelipatan 4 (8 karakter). Base64URL cukup berhenti di 6 karakter.</p>
<h2>Contoh 2: byte yang memunculkan <code>+</code> dan <code>/</code></h2>
<p>Tiga byte sintetik <code>FB FF BF</code> (dalam heksadesimal):</p>
<ul>
<li>Base64 standar: <code>+/+/</code></li>
<li>Base64URL: <code>-_-_</code></li>
</ul>
<p>Contoh berbasis teks: <code>&gt;&gt;&gt;</code> menjadi <code>Pj4+</code> di Base64 standar dan <code>Pj4-</code> di Base64URL.</p>
<p>Bayangkan Anda menaruh <code>+/+/</code> di <code>https://contoh.id/unduh?id=+/+/</code>. Server bisa membaca <code>+</code> sebagai spasi, dan string yang sampai sudah rusak. Versi <code>-_-_</code> tiba utuh tanpa percent-encoding.</p>
<h2>Contoh 3: header JWT sintetik</h2>
<p>Header JSON berikut:</p>
<pre><code class="language-json">{"alg":"HS256","typ":"JWT"}
</code></pre>
<p>di-encode Base64URL tanpa padding menjadi string yang sering Anda lihat di awal token:</p>
<pre><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
</code></pre>
<p>Payload dan signature memakai aturan yang sama, lalu ketiganya digabung dengan titik. Itulah sebabnya Anda tidak pernah melihat <code>+</code>, <code>/</code>, atau <code>=</code> di JWT yang benar.</p>
<h2>Mengembalikan padding saat decode</h2>
<p>Beberapa decoder Base64 standar menolak string tanpa <code>=</code>. Aturannya sederhana: hitung <code>panjang % 4</code>.</p>
<ul>
<li>Sisa 0: tidak perlu apa-apa.</li>
<li>Sisa 2: tambahkan <code>==</code>.</li>
<li>Sisa 3: tambahkan <code>=</code>.</li>
<li>Sisa 1: string tidak valid (biasanya terpotong saat disalin).</li>
</ul>
<p>Contoh: <code>SGFsbw</code> panjangnya 6, sisa 2, jadi menjadi <code>SGFsbw==</code>. Ganti juga <code>-</code> menjadi <code>+</code> dan <code>_</code> menjadi <code>/</code> sebelum memakai decoder standar.</p>
<p>Di Python, semuanya tinggal satu baris:</p>
<pre><code class="language-python">import base64
base64.urlsafe_b64encode(b"&gt;&gt;&gt;").rstrip(b"=")   # b'Pj4-'
base64.urlsafe_b64decode("SGFsbw" + "==")       # b'Halo'
</code></pre>
<p>Di JavaScript modern (browser atau Node), <code>btoa</code> menghasilkan Base64 standar, jadi Anda perlu mengganti karakter sendiri: <code>.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "")</code>.</p>
<h2>Kesalahan yang sering terjadi</h2>
<ol>
<li><strong>Double-encoding.</strong> String Base64URL di-percent-encode lagi, atau di-encode Base64 dua kali. Hasilnya tetap bisa didekode, tetapi isinya string Base64, bukan data asli.</li>
<li><strong>Mencampur alfabet.</strong> Data diencode dengan <code>+</code>/<code>/</code> lalu didekode dengan decoder URL-safe yang ketat, atau sebaliknya. Gejalanya: error "invalid character" di tengah string.</li>
<li><strong>Menganggap Base64URL sebagai penyamaran.</strong> Data sensitif di URL tetap tercatat di log server dan riwayat browser, apa pun encoding-nya.</li>
</ol>
<h2>Cara memakai alat (label UI Bahasa Indonesia)</h2>
<p>Buka <a href="https://base64decodetool.com/id/url-safe/">base64decodetool.com/id/url-safe/</a>. Di halaman ini, sakelar <strong>Format Aman URL</strong> sudah aktif dan terkunci, jadi hasil encode otomatis memakai <code>-</code>/<code>_</code> tanpa <code>=</code>.</p>
<ol>
<li>Pilih tab <strong>Enkode ke Base64</strong> (atau <strong>Dekode Base64</strong> untuk arah sebaliknya).</li>
<li>Biarkan <strong>Set Karakter</strong> di UTF-8 untuk teks, atau pilih <strong>Hexadecimal</strong> lalu ketik <code>fbffbf</code> untuk mengulang Contoh 2.</li>
<li>Ketik input, misalnya <code>Halo</code> atau <code>&gt;&gt;&gt;</code>, lalu tekan <strong>Konversi</strong>.</li>
<li>Periksa <strong>String Output</strong>, lalu tekan <strong>Salin</strong>.</li>
</ol>
<p>Semua proses berjalan di browser Anda; string tidak dikirim ke server alat.</p>
<h2>Kapan memakai yang mana</h2>
<ul>
<li><strong>Base64URL</strong>: segmen JWT, token di URL, tautan reset kata sandi, WebAuthn, ID di path atau query.</li>
<li><strong>Base64 standar</strong>: data URI (<code>data:image/png;base64,...</code>), lampiran MIME email, dan file konfigurasi yang tidak hidup di URL.</li>
</ul>
<h2>Ringkasan</h2>
<p>Base64URL adalah Base64 dengan <code>-</code> dan <code>_</code> menggantikan <code>+</code> dan <code>/</code>, biasanya tanpa <code>=</code>. Gunakan untuk apa pun yang lewat URL atau JWT, kembalikan padding dengan aturan <code>panjang % 4</code> saat decode, dan ingat bahwa ini tetap pengodean, bukan keamanan. Untuk latihan, ulangi tiga contoh sintetik di atas di <a href="https://base64decodetool.com/id/url-safe/">alat Base64 aman URL</a>.</p>
]]></content:encoded></item></channel></rss>