Encode and decode Base64, JWT tokens, URLs, and HTML entities online for free. Understand when and why to use each encoding — with practical examples.
Every developer hits the same wall at some point. You're debugging an API response and the payload is a long string of characters that looks like someone fell asleep on the keyboard. Or you're reading a URL with %20 and %3D scattered through it, and you need to figure out what the original value was. Or someone pastes a JWT token in Slack and asks "what's in this?" and you don't have a tool open.
Encoding and decoding are the plumbing of web development. They're not glamorous, but when you need them, you need them immediately. And if you get them wrong — if you double-encode a URL, or forget to encode HTML entities before rendering user input — you'll spend hours debugging issues that could have been avoided.
This guide covers the four encoding types you'll use most often: Base64, JWT, URL encoding, and HTML entity encoding. For each one, I'll explain what it does, when you need it, the common mistakes, and how to do it in code. And if you just need to quickly encode or decode something right now, akousa.net's developer tools will handle it in your browser — no sign-up, no data sent to a server.
Base64 is a binary-to-text encoding scheme that converts binary data into a string of 64 ASCII characters (A-Z, a-z, 0-9, +, /, and = for padding). It exists because many systems — email, JSON, URLs — can only handle text safely, not raw bytes.
When you Base64-encode a file, you're not encrypting it. You're not compressing it. You're translating it into a format that text-based protocols can carry without corrupting it. The output is roughly 33% larger than the input.
Authorization: Basic header encodes username:password in Base64 (which is why Basic auth without HTTPS is dangerously insecure — it's not encrypted, just encoded).JavaScript / Node.js:
// Encode
const encoded = btoa("Hello, World!");
// "SGVsbG8sIFdvcmxkIQ=="
// Decode
const decoded = atob("SGVsbG8sIFdvcmxkIQ==");
// "Hello, World!"
// For binary data in Node.js
const buffer = Buffer.from("Hello, World!");
const base64 = buffer.toString("base64");
const original = Buffer.from(base64, "base64").toString("utf-8");Python:
import base64
# Encode
encoded = base64.b64encode(b"Hello, World!").decode("utf-8")
# "SGVsbG8sIFdvcmxkIQ=="
# Decode
decoded = base64.b64decode("SGVsbG8sIFdvcmxkIQ==").decode("utf-8")
# "Hello, World!"Go:
package main
import (
"encoding/base64"
"fmt"
)
func main() {
// Encode
encoded := base64.StdEncoding.EncodeToString([]byte("Hello, World!"))
fmt.Println(encoded) // SGVsbG8sIFdvcmxkIQ==
// Decode
decoded, _ := base64.StdEncoding.DecodeString(encoded)
fmt.Println(string(decoded)) // Hello, World!
}Treating it as encryption. Base64 is reversible by anyone. Never use it to "hide" sensitive data. I've seen API keys, passwords, and private certificates stored in Base64 with comments like "encrypted for security." They weren't.
Double encoding. If your framework already Base64-encodes a value and you encode it again, you'll get a valid Base64 string that decodes to... another Base64 string. This happens more often than you'd think, especially with file upload handlers.
Ignoring URL-safe variants. Standard Base64 uses + and /, which have special meanings in URLs. If you're putting Base64 in a URL, use the URL-safe variant (- and _ instead). Most languages have a built-in function for this.
Need to quickly encode or decode Base64 without writing code? The Base64 encoder/decoder on akousa.net handles text and file conversions instantly in your browser. For images specifically, the Image to Base64 converter generates ready-to-use data URIs.
A JSON Web Token is a compact, URL-safe way to represent claims between two parties. It's the dominant format for authentication tokens in modern web applications. A JWT has three parts separated by dots:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ.Qf3kj3nF_xAm5LdQ1sR7cXzBuwDmVE_O-rI1kW2mFMk
Each part is Base64URL-encoded JSON:
HS256, RS256).The critical thing to understand: the payload is not encrypted. Anyone can decode the payload and read its contents. The signature only guarantees integrity — that the data hasn't been modified — not confidentiality. Don't put sensitive information (passwords, credit card numbers, SSNs) in JWT payloads.
sub claim what you expect?localStorage.iat (issued at) and exp (expiration) timestamps with your server's clock.JavaScript:
function decodeJWT(token) {
const parts = token.split(".");
if (parts.length !== 3) throw new Error("Invalid JWT format");
const header = JSON.parse(atob(parts[0].replace(/-/g, "+").replace(/_/g, "/")));
const payload = JSON.parse(atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")));
return { header, payload, signature: parts[2] };
}
// Usage
const { header, payload } = decodeJWT(token);
console.log("Algorithm:", header.alg);
console.log("Expires:", new Date(payload.exp * 1000));Python:
import json
import base64
def decode_jwt(token):
parts = token.split(".")
if len(parts) != 3:
raise ValueError("Invalid JWT format")
# Add padding if needed
def decode_part(part):
padding = 4 - len(part) % 4
part += "=" * padding
return json.loads(base64.urlsafe_b64decode(part))
return {
"header": decode_part(parts[0]),
"payload": decode_part(parts[1]),
}Important: Decoding is not the same as verification. Never trust a decoded JWT payload without verifying the signature against your secret or public key. Decoding tells you what the token says. Verification tells you whether to believe it.
For quick inspection during development, the JWT Decoder on akousa.net breaks down the header, payload, and signature visually, shows expiration status, and formats timestamps into human-readable dates. The JWT Inspector goes deeper with claim validation and structure analysis. If you need to generate test tokens, the JWT Generator lets you build them with custom claims.
URLs can only contain a specific set of characters defined in RFC 3986. When a URL needs to include characters outside that set — spaces, ampersands, equals signs, Unicode characters, or anything that could be misinterpreted as a URL delimiter — those characters must be percent-encoded: replaced with a % followed by their hexadecimal byte value.
Space → %20
& → %26
= → %3D
/ → %2F
日本語 → %E6%97%A5%E6%9C%AC%E8%AA%9E
& or =, it'll break the URL structure unless encoded. ?q=salt & pepper becomes ?q=salt%20%26%20pepper.application/x-www-form-urlencoded content type.JavaScript:
// Encode a full URI component (query parameter value)
encodeURIComponent("hello world & goodbye");
// "hello%20world%20%26%20goodbye"
// Encode a full URI (preserves :, /, ?, #, etc.)
encodeURI("https://example.com/path?q=hello world");
// "https://example.com/path?q=hello%20world"
// Decode
decodeURIComponent("hello%20world%20%26%20goodbye");
// "hello world & goodbye"Python:
from urllib.parse import quote, unquote, urlencode
# Encode a single value
quote("hello world & goodbye")
# "hello%20world%20%26%20goodbye"
# Build a query string from a dictionary
urlencode({"q": "salt & pepper", "page": 2})
# "q=salt+%26+pepper&page=2"
# Decode
unquote("hello%20world%20%26%20goodbye")
# "hello world & goodbye"Java:
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
// Encode
String encoded = URLEncoder.encode("hello world & goodbye", StandardCharsets.UTF_8);
// "hello+world+%26+goodbye"
// Decode
String decoded = URLDecoder.decode(encoded, StandardCharsets.UTF_8);
// "hello world & goodbye"The most common mistake is encoding an entire URL instead of just the dynamic parts. If you pass https://example.com/search?q=test through encodeURIComponent(), you'll get https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dtest — a completely broken URL. Use encodeURIComponent() for individual parameter values, and encodeURI() (or just don't encode) for the full URL structure.
Another frequent issue: double encoding. Your framework encodes the value, then you encode it again. The % from the first encoding gets encoded to %25, and now %20 becomes %2520. If you see %25 in your URLs, you've almost certainly double-encoded something.
The URL Encoder tool on akousa.net lets you encode and decode URL components instantly, which is especially handy when you're debugging a URL with multiple layers of encoding and need to peel them apart one at a time.
HTML entity encoding replaces characters that have special meaning in HTML with named or numeric references. The most important ones:
< → <
> → >
& → &
" → "
' → ' (or ' in XHTML)
This exists for two reasons: correctness (so the browser doesn't interpret your data as HTML tags) and security (so attackers can't inject scripts into your page).
<script>alert('xss')</script> can execute JavaScript in every other user's browser.dangerouslySetInnerHTML, v-html, or [innerHTML], you're bypassing that protection.<input value="user's "data""> is malformed. Encoding fixes it: <input value="user's "data"">.JavaScript (browser):
// Encode (simple approach using DOM)
function encodeHTML(str) {
const div = document.createElement("div");
div.textContent = str;
return div.innerHTML;
}
encodeHTML('<script>alert("xss")</script>');
// "<script>alert("xss")</script>"
// Decode
function decodeHTML(str) {
const div = document.createElement("div");
div.innerHTML = str;
return div.textContent;
}Python:
import html
# Encode
html.escape('<script>alert("xss")</script>')
# '<script>alert("xss")</script>'
# Decode
html.unescape('<b>bold</b>')
# '<b>bold</b>'PHP:
// Encode
htmlspecialchars('<script>alert("xss")</script>', ENT_QUOTES, 'UTF-8');
// "<script>alert("xss")</script>"
// Decode
htmlspecialchars_decode('<b>bold</b>', ENT_QUOTES);
// "<b>bold</b>"HTML entity encoding is the difference between a secure application and a headline-making data breach. XSS consistently ranks in the OWASP Top 10, and the root cause is almost always "user input was rendered without encoding."
The rule is simple: encode on output, not on input. Store the raw data in your database. Encode it at the moment you render it in HTML. This gives you flexibility — the same data might be rendered in HTML, in a JSON API response, or in a PDF, and each context needs different encoding.
The HTML Entity Encoder on akousa.net handles both encoding and decoding, which is useful when you're inspecting HTML source code that's been entity-encoded and you need to read the original content.
| Feature | Base64 | JWT | URL Encoding | HTML Entities |
|---|---|---|---|---|
| Purpose | Binary-to-text conversion | Structured authentication tokens | Safe characters in URLs | Safe characters in HTML |
| Reversible by anyone? | Yes | Payload: yes. Signature: no | Yes | Yes |
| Provides security? | No | Integrity only (not confidentiality) | No | Prevents XSS |
| Size impact | ~33% larger | Varies (typically 200-800 bytes) | ~3x per special char | ~5-6x per special char |
| Character set | A-Z, a-z, 0-9, +, /, = | Base64URL (A-Z, a-z, 0-9, -, _) | Unreserved chars pass through | Named entities or &#NN; |
| Standard | RFC 4648 | RFC 7519 | RFC 3986 | HTML Living Standard |
| Common use case | Embed images, send binary in JSON | API authentication, SSO | Query parameters, form data | Render user content safely |
| Tool on akousa.net | Base64 Tool | JWT Decoder | URL Encoder | HTML Entity Encoder |
In real applications, these encoding types often appear together. Understanding how they stack is essential for debugging.
JWT in a URL parameter: A JWT is already Base64URL-encoded. If you put it in a URL query parameter, you typically don't need to URL-encode it again — the Base64URL alphabet was designed to be URL-safe. But if you're putting it in a form field that gets URL-encoded on submission, the dots in the JWT will survive (dots are unreserved in URLs), so it still works.
Base64 in a URL: Standard Base64 uses + and /, which have meaning in URLs. Either use URL-safe Base64 (- and _), or URL-encode the standard Base64 string. Don't just hope it works — it won't.
HTML containing URLs with encoded parameters: Consider an anchor tag with a URL that has encoded query parameters:
<a href="https://example.com/search?q=salt%20%26%20pepper">Search</a>The %26 is a URL-encoded &. The browser will URL-decode it when following the link. But if this HTML was generated by server-side code, the & in the href attribute should technically be & for valid HTML:
<a href="https://example.com/search?q=salt%20%26%20pepper&page=2">Search</a>This is the kind of layered encoding that causes subtle bugs. Having reliable tools to peel apart each layer makes debugging tractable. The developer tools suite on akousa.net includes all four encoding types, so you can chain them: decode the HTML entities first, then URL-decode the result, then Base64-decode the inner value.
Encode at the boundary. Don't encode data when you receive it. Encode it when you output it, and use the encoding appropriate for the output context (HTML, URL, JSON, etc.).
Use framework defaults. React escapes JSX content by default. Django auto-escapes templates. Rails escapes ERB output. Don't disable these features unless you have a specific, well-understood reason.
Never use Base64 for security. It's encoding, not encryption. If you need to protect data, use AES-256-GCM or a similar authenticated encryption scheme.
Validate JWTs on the server. Decode the payload for display purposes, but always verify the signature before trusting the claims. Use a well-maintained library — don't write your own JWT verification.
Test with adversarial inputs. Put <script>, '; DROP TABLE, ../../etc/passwd, and emoji in every input field. If your encoding is correct, none of these will cause problems.
Use URL-safe Base64 when the output goes in a URL. Standard Base64 with + and / will break. Every language has a URL-safe variant — use it.
No. Base64 encoding is completely reversible by anyone without any key or secret. It is a binary-to-text conversion, not a security mechanism. If you need to protect data, use proper encryption algorithms like AES-256-GCM. Base64 should only be used to transport binary data through text-based channels like JSON, HTML, or email.
Yes, you can decode the header and payload of any JWT without the secret key, because they are simply Base64URL-encoded JSON. The secret key is only needed to verify the signature — to confirm that the token hasn't been tampered with. This is why you should never put sensitive information (passwords, credit card numbers) in JWT payloads. Tools like the JWT Decoder on akousa.net can decode any JWT instantly.
encodeURI() and encodeURIComponent() in JavaScript?#encodeURI() encodes a complete URI and preserves characters that have structural meaning in URLs (:, /, ?, #, &, =). encodeURIComponent() encodes everything except unreserved characters, making it suitable for encoding individual parameter values. Use encodeURIComponent() for values you're inserting into a query string, and encodeURI() only when you need to encode an entire URL while preserving its structure.
%2520 in my URLs?#%2520 means a space character has been URL-encoded twice. The first encoding turns the space into %20. The second encoding turns the % into %25, resulting in %2520. This happens when your code URL-encodes a value, and then the framework or HTTP client encodes it again. The fix is to encode only once, usually by letting the framework handle the encoding or by encoding manually — but not both.
HTML entity encoding is the primary defense against XSS when rendering user input in HTML body content. However, it's not sufficient for all contexts. If you're inserting user input into JavaScript code, CSS, URL attributes (href, src), or event handler attributes (onclick), you need context-specific encoding or sanitization. A defense-in-depth approach combines output encoding, Content Security Policy (CSP) headers, and input validation.
Encoding and decoding are not exciting topics, but they're foundational. Every web application you build will need at least two of these encoding types, and most will need all four. Understanding what each one does — and critically, what it doesn't do — saves hours of debugging and prevents security vulnerabilities.
When you need to quickly encode or decode something during development, bookmark the developer tools on akousa.net. The Base64 tool, JWT Decoder, URL Encoder, and HTML Entity Encoder all run entirely in your browser — your data never leaves your machine.