# URL Encode / Decode

> Percent-encode or decode text for URLs, query strings, and form bodies.

Category: Encoding · Page: https://util.caruana.me/tools/url/

## API, MCP and A2A

### URL: Encode (`url_encode`)

`GET|POST https://util.caruana.me/api/url/encode` · MCP tool `url_encode` · A2A skill `url_encode` · [reference](https://util.caruana.me/reference/url_encode/)

Percent-encodes the UTF-8 bytes of `input`. `component` matches JavaScript encodeURIComponent, `uri` matches encodeURI, and `form` is application/x-www-form-urlencoded (spaces become `+`).

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `input` | string | yes | Text to convert. As a `text/plain` POST body it is used verbatim. |
| `mode` | component \| uri \| form | no | `component`: encodeURIComponent — escapes everything except A–Z a–z 0–9 - _ . ! ~ * ' ( ). `uri`: encodeURI — keeps URL structure (: / ? # & = etc.) intact. `form`: application/x-www-form-urlencoded — like Component, but spaces become + |

Privacy: The server receives the `input` text and `mode`, converts it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. Send secrets in a POST body, not the query string: URLs are recorded in Cloudflare request logs, proxies, and shell history.

### URL: Decode (`url_decode`)

`GET|POST https://util.caruana.me/api/url/decode` · MCP tool `url_decode` · A2A skill `url_decode` · [reference](https://util.caruana.me/reference/url_decode/)

Decodes `%XX` sequences as UTF-8. In `form` mode `+` is first turned into a space.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `input` | string | yes | Text to convert. As a `text/plain` POST body it is used verbatim. |
| `mode` | component \| uri \| form | no | `component`: encodeURIComponent — escapes everything except A–Z a–z 0–9 - _ . ! ~ * ' ( ). `uri`: encodeURI — keeps URL structure (: / ? # & = etc.) intact. `form`: application/x-www-form-urlencoded — like Component, but spaces become + |

Privacy: The server receives the `input` text and `mode`, converts it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. Send secrets in a POST body, not the query string: URLs are recorded in Cloudflare request logs, proxies, and shell history.


## User guide

The tool sits in a window that works like a desktop app: menus on the top row and the **full-screen** icon at its right (**Esc** leaves full screen). The set-up options described below are in **Settings → Options…**, an overlay, and the **Settings** menu also lists the common choices directly. To keep the options in view instead, choose **View → Inline config**; the page remembers that choice. **Help → Keyboard shortcuts…** lists the keys.

1. Choose **Encode** or **Decode**.
2. Choose a **mode**. If you are unsure, use **Component**.
3. Type or paste into **Input**. The output updates on every keystroke.
4. **Copy** puts the output on your clipboard. **Use output as input** swaps the panes and flips the direction, which is a quick way to check a round trip.

**Which mode do I need?**

| You are encoding…                                       | Mode      | Example input → output                                 |
| ------------------------------------------------------- | --------- | ------------------------------------------------------ |
| One query-string value or path segment                  | Component | `a&b=c` → `a%26b%3Dc`                                  |
| A whole URL that is already structured                  | Full URI  | `https://x.io/a b?q=é` → `https://x.io/a%20b?q=%C3%A9` |
| An HTML form body (`application/x-www-form-urlencoded`) | Form      | `a b+c` → `a+b%2Bc`                                    |

> Never encode a whole URL with **Component**: it escapes `:` `/` `?` `&` and breaks the URL. Never build query strings with **Full URI**: it leaves `&` and `=` alone, so a value containing them changes the meaning of the query.

**Decoding tips.** Decode with the same mode that was used to encode. In particular, `+` means a space only in **Form** mode; in the other modes it stays a literal `+`. If you see _malformed % sequence_, the text contains a `%` not followed by two hex digits (for example `100%`), or bytes that are not valid UTF-8.

## Specification

**Character encoding.** Text is converted to UTF-8 first. Each byte outside the unreserved set becomes `%XX` with uppercase hex digits (RFC 3986 §2.1).

| Mode        | Encoding behaviour                     | Characters left as-is                  |
| ----------- | -------------------------------------- | -------------------------------------- |
| `component` | ECMAScript `encodeURIComponent`        | `A–Z a–z 0–9 - _ . ! ~ * ' ( )`        |
| `uri`       | ECMAScript `encodeURI`                 | the above plus `; , / ? : @ & = + $ #` |
| `form`      | `encodeURIComponent`, then `%20` → `+` | as `component`                         |

| Mode        | Decoding behaviour                                                                                                        |
| ----------- | ------------------------------------------------------------------------------------------------------------------------- |
| `component` | `decodeURIComponent`: every `%XX` is decoded.                                                                             |
| `uri`       | `decodeURI`: escapes for reserved characters (`%2F`, `%3F`, `%26`, …) are **kept**, so the URL's structure cannot change. |
| `form`      | every `+` becomes a space, then `decodeURIComponent`.                                                                     |

**Errors.**

- **Encode** fails only for a lone UTF-16 surrogate, which cannot be represented in UTF-8.
- **Decode** fails when a `%` is not followed by two hex digits, or when the decoded bytes are not valid UTF-8.

**Differences from other tools.**

- `form` mode matches the WHATWG URL standard's `application/x-www-form-urlencoded` serializer, except that `! ' ( ) ~` are left unescaped (WHATWG escapes them).
- Other encoders differ on the same characters; for example, PHP's `urlencode` escapes all five. Every variant decodes to the same text.

**Limits.** None in the browser beyond memory. The API accepts request bodies up to 1 MiB.
