URL Encoder

Percent-encode a single value, a query string or a whole URL, without encoding it twice, and decode escapes back to text with the position of anything that is wrong.

Overview

A URL may only contain a small set of ASCII characters, and some of them have a meaning: / separates path segments, ? starts the query, & separates its pairs, # starts the fragment. Percent-encoding, defined in RFC 3986, writes every other character, and every character that should lose its meaning, as % followed by the two hex digits of each of its UTF-8 bytes. A space becomes %20, & becomes %26, ü becomes %C3%BC.

RFC 3986 calls A to Z, a to z, 0 to 9 and - . _ ~ unreserved: they never need encoding. The reserved characters : / ? # [ ] @ ! $ & ' ( ) * + , ; = are left alone where they have their meaning and encoded where they are data. Which of the two applies depends on where a character stands, which is why the tool has three encoding modes.

Web interface

Paste a URL, text or an encoded string into the input; the output updates as you type. Auto works out the direction by itself, Encode and Decode fix it. When encoding, Encoding chooses Component, Query String or Full URL. When decoding, Plus sign chooses whether a + stays as it is or stands for a space. Each row is only shown when it applies.

The line under the output gives the length before and after, counted in characters as a reader counts them, so an emoji is one. Copy copies the output. Clear empties everything and returns to Auto, Component and Keep. Ctrl/ + Enter runs the conversion again.

How Auto decides

Auto decodes input that contains at least one %XX escape and no % that starts no escape. Everything else is encoded.

InputAutoOutput
caf%C3%A9decodescafé
hello worldencodeshello%20world
100% sure %20encodes, because % s is no escape100%25%20sure%20%2520
%E9decodes, and reports that the byte is not UTF-8none

Input with escapes that are not UTF-8 is decoded rather than encoded, so the error says what is wrong instead of the input being encoded a second time. When Auto guesses wrong, Encode and Decode settle it.

Encoding modes

The same input, https://example.com/a b?q=ü&next=/x?y=1#top, in each mode:

ModeOutput
Componenthttps%3A%2F%2Fexample.com%2Fa%20b%3Fq%3D%C3%BC%26next%3D%2Fx%3Fy%3D1%23top
Query Stringhttps://example.com/a%20b?q=%C3%BC&next=%2Fx%3Fy%3D1#top
Full URLhttps://example.com/a%20b?q=%C3%BC&next=/x?y=1#top

Component is for a single value, such as one query parameter or one path segment. It encodes everything except A-Z a-z 0-9 - _ . ~ ! * ' ( ), including %, so text that is already encoded is encoded again. That is what a value needs that really contains %20. It is what JavaScript's encodeURIComponent does.

Query String is for key=value pairs. The part before the first ? is encoded as in Full URL. The rest is split at every &, and each pair at its first =; key and value are then encoded like Component, except that existing %XX escapes stay. A second ? or = is part of the value and is encoded, nothing is dropped. A # starts the fragment, which is kept. Input without a ? is taken as a query string itself, unless it starts with a scheme such as https:// or with /; then it is a URL without a query and is encoded as in Full URL.

Full URL is for a whole address. It encodes only what may not appear in a URL: spaces, non-ASCII characters, control characters, " < > \ ^ ` { | }, and a % that starts no escape. Reserved characters and existing %XX escapes stay, so an encoded URL comes back unchanged and [ ] around an IPv6 address survive.

All three modes write a space as %20, never as +, and Query String encodes a literal + as %2B, so the result means the same to a form-data decoder and to everything else.

Decoding

Decoding turns each run of %XX escapes back into bytes and reads them as UTF-8. Hex digits may be upper or lower case. Everything that is not an escape stays as it is.

A + means a space only in form data, the application/x-www-form-urlencoded format of HTML forms. Anywhere else in a URL it is a plus sign. Plus sign: Keep, the default, leaves it alone; As space turns it into a space. %2B becomes + in both cases.

Decoding runs once. Input that was encoded twice, %2520, decodes to %20; paste the output back in to decode the second layer.

Errors

Input that cannot be decoded is not decoded half-way. The output stays empty and the line under it says where the problem is, counted in characters from 1:

MessageMeaning
the % at position 4 is not followed by two hex digitsA % that starts no escape, such as a percent sign in text or an escape cut off at the end.
the escapes at position 1 (%E9) are not valid UTF-8The bytes are not UTF-8. Usually the text was encoded as Latin-1 or Windows-1252, where é is %E9 instead of %C3%A9.
the text contains a lone surrogateWhen encoding: the input holds half of a character, such as half of an emoji left by a broken copy. It has no UTF-8 form.

No API

There is no API for this tool. Encoding and decoding run in the browser; in a script, use the function of the language at hand, such as encodeURIComponent in JavaScript, urllib.parse.quote in Python or rawurlencode in PHP.

Limits

The input has no length limit. The output is recalculated on every keystroke, which stays unnoticeable up to about a megabyte: 1 MB took under a second in every browser tested, drawing included.

Converting itself is fast: 10 MB take under one second in every browser tested. Drawing that much text in the input and output fields is what takes time, and it depends on the browser. For 10 MB it took 1.5 to 2.5 seconds in Chrome and Firefox and up to about 19 seconds in Safari's engine.

Privacy

Nothing leaves the browser. The conversion runs on the page, the input is not put into the page address, nothing is stored, and Copy writes to the clipboard only when you press it.