URL Encode a CSV Column

Percent-encoding replaces the characters that mean something structural in a URL with a percent sign and two hex digits. Which characters those are depends on where the value is going, so this page offers three encodings rather than one: inside a query parameter, across a whole URL, or in form data. It runs in this browser tab.

Pulling the pieces out of a URL instead? Extract them with a pattern.

"URL encode" means three different things

Most tools offer one button. That works until the value is a URL rather than a piece of one, and then it produces something unusable, because the two cases want opposite behavior from the same characters.

  • Query component. The value is going inside a parameter, so every structural character has to be escaped or it will break out: / ? : @ & = + $ # all become percent sequences. This is what encodeURIComponent does and it is the default here, because escaping too much in a query string is harmless and escaping too little is a bug.
  • Whole URL. The value already is an address, so those same characters are structure and must survive. Only the genuinely unsafe things get escaped: spaces, accented characters, quotes. This is encodeURI. Run a URL through component encoding by mistake and you get https%3A%2F%2Fexample.com, which is not a link any more.
  • Form. The application/x-www-form-urlencoded dialect that HTML forms and most server-side parsers actually produce. Component encoding plus two differences: a space is + rather than %20, and the characters ! ' ( ) * are escaped too, which encodeURIComponent leaves alone.

The same three choices apply to decoding, and picking the wrong one there is how a plus sign in someone's data quietly becomes a space.

Worked example: the same URL, three ways

The example button loads requests.csv, an access log:

request_id,path,status,logged_at
r-8891,/api/v2/orders?id=1204&format=csv,200,2024-01-15T09:14:02Z
r-8892,/api/v2/customers?id=88,404,2024-01-15T09:15:41Z

Tick path and watch what each encoding does to the first row:

source           /api/v2/orders?id=1204&format=csv

query component  %2Fapi%2Fv2%2Forders%3Fid%3D1204%26format%3Dcsv
whole URL        /api/v2/orders?id=1204&format=csv
form             %2Fapi%2Fv2%2Forders%3Fid%3D1204%26format%3Dcsv

Whole URL leaves it completely alone, because every character in it is legal structure. Component encoding escapes all of it, which is exactly right if this path is about to be stuffed into a ?redirect= parameter and exactly wrong if it is meant to stay a path.

The difference between component and form only shows up once there is a space in the value:

source           search terms

query component  search%20terms
form             search+terms

Both are correct in their own context and neither is correct in the other. If a downstream system is showing you literal plus signs where spaces should be, it decoded form data with a component decoder, and switching this control is the fix.

Decoding, and the sequence that throws

decodeURIComponent throws a URIError on a malformed percent sequence. Not returns null, not leaves it alone: throws. %E0%A4%A, which is a three-byte UTF-8 character that lost its last digit somewhere in a log rotation, will take down a naive loop on row 40,000 of a file that had been decoding perfectly.

Here each cell is decoded on its own and a failure is caught. That row keeps its original text, the count goes up, and the first few failing values are quoted back in the warning above the table. Thirty-nine thousand nine hundred and ninety-nine rows still decode.

Two things that cause it in real files. Truncation, where a log or a database column cut a value mid-sequence. And double encoding, where a value was encoded twice and a single decode leaves you with %2520 style leftovers; that one does not throw, it just decodes to %20, so run the column through a second time and watch the change count.

A percent sign that was never part of an escape at all, as in 50% off, is the other common one. It is not valid percent-encoded text, it is reported as such, and the cell is left as found rather than being half-decoded.

Which one do I want?

A short decision list, since this is the only real question on the page.

  • Building a query string out of values in a column: query component. Anything else risks a value containing an ampersand and silently becoming two parameters.
  • Cleaning up a column of URLs so they are safe to put in an href or hand to a crawler: whole URL. It fixes the spaces and the accented characters and leaves the address working.
  • Recreating a form submission, or matching against values that came out of one: form.
  • Reading a log where the paths are encoded and you want them legible: decode with whole URL, which leaves the structure alone and turns the escaped characters back into text.
  • Reading query parameter values that were pulled out of a log: decode with component, or with form if the source system wrote spaces as plus signs.

When you are not sure, encode and then decode with the same setting and check you got the original back. The result recomputes on every control change, so that round trip is four clicks.

Practical notes

  • Non-ASCII is handled as UTF-8. An accented character becomes two or three percent sequences, which is what the standard requires and what every server expects. café in a query component is caf%C3%A9.
  • Blank cells stay blank and are counted separately rather than becoming empty strings that look encoded.
  • The new column sits beside its source, named <column>_encoded or <column>_decoded, so the pair reads together. Switch Original column to replace it when you want the file to keep its shape.
  • Nothing else in the row is touched. A leading zero in an id column is still a leading zero, because cells are strings from read to write.
  • Quoting is handled on the way out. An encoded value cannot contain a comma or a quote, but a decoded one very much can, and the CSV writer escapes it correctly so the file still parses.

Frequently Asked Questions

What is the difference between the three encodings?

Query component escapes every structural character including slash, question mark and ampersand, which is right for a value going inside a parameter. Whole URL leaves those alone and escapes only spaces and non-ASCII, which is right when the value already is an address. Form is component encoding with a space written as a plus sign and a few extra characters escaped.

Why did my URL come back as https%3A%2F%2F...?

Because it was encoded as a query component rather than as a whole URL. Component encoding escapes the colon and the slashes, which is correct when the address is about to be embedded in a redirect parameter and wrong when it is meant to stay a working link. Switch Encoding to Whole URL.

When should I use the form encoding?

When you are recreating an HTML form submission, or matching values that came out of one. It writes a space as a plus sign, which is what application/x-www-form-urlencoded does. If a downstream system is showing literal plus signs where spaces should be, it decoded form data with a component decoder.

What happens to a malformed percent sequence?

The cell keeps its original text and is counted, with the first few quoted back in the warning. Decoding a broken sequence such as %E0%A4%A throws a URIError in JavaScript, so a naive loop would stop dead on that row. Here each cell is decoded on its own and everything else still converts.

Why does 50% off fail to decode?

Because a percent sign followed by something that is not two hex digits is not valid percent-encoded text. Rather than half-decoding it or throwing, the cell is left exactly as found and counted, which tells you the column holds plain text rather than encoded text.

My value has %2520 in it after decoding. What happened?

It was encoded twice. A single decode turns %2520 into %20, so run the column through the decoder a second time and watch the change count. Double encoding usually means a value passed through two layers that each assumed the other had not encoded it.

Does it handle accented characters?

Yes. Non-ASCII is encoded as UTF-8 first, so an accented character becomes two or three percent sequences, which is what the standard requires and what any server will expect. Decoding reverses it exactly.

Is the file uploaded anywhere?

No. There is no upload endpoint on this page. The file is read, encoded and rewritten by JavaScript in your own tab, and nothing is kept between visits.

Escape it for where it is going

Free, no account, no upload. Pick the column, pick the encoding, check the round trip, take the CSV.

Back to the URL encoder