Article · Apr 8, 2026 · 5 min read

URL encoding in cURL

cURL is the most popular HTTP command-line tool, and it knows about URL encoding — but it doesn’t encode automatically. Slash-newcomer mistakes like curl "https://api.example.com/search?q=hello world" fail silently or produce unexpected results.

This article covers cURL’s URL encoding options, how to build correctly encoded GET and POST requests, and the common pitfalls.

The --data-urlencode flag

The simplest correct approach uses --data-urlencode:

curl --data-urlencode "q=Hello, World!" \
     --data-urlencode "lang=en" \
     -G https://api.example.com/search

The -G (or --get) flag tells cURL to make this a GET request with the data appended as a query string instead of sent as a POST body. cURL encodes each key/value pair correctly.

Result: cURL makes a GET request to:

https://api.example.com/search?q=Hello%2C%20World%21&lang=en

(actually with + for the space — let me verify this and add the correct details)

How --data-urlencode encodes data

cURL’s --data-urlencode produces RFC 3986 strict encoding by default. Space becomes %20, exclamation becomes %21, comma becomes %2C. This is the modern standard.

The flag has four forms based on what you pass to it:

# Plain value (just URL-encodes the whole thing)
curl --data-urlencode "Hello, World!" https://example.com

# key=value (URL-encodes only the value, not the key)
curl --data-urlencode "q=Hello, World!" https://example.com

# key@filename (reads value from file, URL-encodes it)
curl --data-urlencode "message@email.txt" https://example.com

# =value (URL-encodes the value, no key prefix)
curl --data-urlencode "=Hello, World!" https://example.com

POST requests with URL-encoded body

Without -G, the same flags become a POST request with application/x-www-form-urlencoded body:

curl -X POST https://api.example.com/submit \
     --data-urlencode "name=John Doe" \
     --data-urlencode "message=Hello, World!"

# Sends:
# POST /submit HTTP/1.1
# Content-Type: application/x-www-form-urlencoded
# Content-Length: ...
#
# name=John%20Doe&message=Hello%2C%20World%21

The wrong way: --data (without -urlencode)

The plain -d or --data flag does NOT encode:

# Wrong
curl -d "q=Hello, World!" https://api.example.com/search

# This sends literal "q=Hello, World!" — the comma and space are unencoded.
# Depending on the server, this either works (forgiving servers) or fails
# with a 400 (strict ones).

If you’re building a one-off script, use --data-urlencode instead of -d whenever your value might contain special characters.

GET requests: -G changes everything

The -G flag is required to make --data-urlencode produce a GET request. Without it, the data goes in the request body (which is unusual for GET):

# Without -G: produces a GET... with a body? Rarely what you want.
curl --data-urlencode "q=hello" https://api.example.com/search

# With -G: produces a clean GET with query string
curl --data-urlencode "q=hello" -G https://api.example.com/search

Encoding the URL itself

If the base URL contains special characters in the path, you have to encode them yourself before passing to cURL:

# A path with a space
curl "https://example.com/My Document"
# cURL passes this through literally — most servers will accept it
# (browsers do, too) but it’s technically invalid

# Encoded version (more reliable)
curl "https://example.com/My%20Document"

cURL doesn’t auto-encode the URL itself — only the data parameters when you use --data-urlencode.

The shell-quoting trap

If you don’t quote your URLs, the shell breaks them up at spaces and special chars:

# Wrong — the & backgrounds the curl process
curl https://example.com/search?q=hello&lang=en

# Right — quote the whole URL
curl "https://example.com/search?q=hello&lang=en"

# Or single-quote (no variable expansion)
curl 'https://example.com/search?q=hello&lang=en'

Always quote URLs in shell commands.

Reading values from files

The @filename syntax reads a value from a file (useful for large inputs):

# message.txt contains: Hello, World! <script>alert("xss")</script>
curl --data-urlencode "message@message.txt" -G https://api.example.com/log

# cURL reads message.txt, URL-encodes the content, and sends it as ?message=...

JSON bodies (when URL encoding doesn’t apply)

If your API takes JSON, you don’t want URL encoding at all — JSON has its own encoding:

curl -X POST https://api.example.com/submit \
     -H "Content-Type: application/json" \
     -d '{"name": "John Doe", "message": "Hello, World!"}'

Note: -d (not --data-urlencode) and the Content-Type header set explicitly.

Common cURL URL encoding mistakes

Mistake 1: Using --data instead of --data-urlencode for query strings

# Wrong — special chars in user input break the URL
curl "https://api.example.com/search?q=$(echo $user_input)"

# Right
curl --data-urlencode "q=$user_input" -G "https://api.example.com/search"

Mistake 2: Forgetting -G for GET requests

Without -G, cURL puts the data in the request body, making a POST-ish GET request that confuses some servers.

Mistake 3: Not quoting URLs

The most common mistake. Always wrap URLs in "..." or '...' when calling cURL from a shell.

Quick reference

Goal Flag
GET with query string--data-urlencode "k=v" -G URL
POST form-encoded body--data-urlencode "k=v" URL
POST JSON body-H "Content-Type: application/json" -d '...'
Read value from file--data-urlencode "k@file"
Don’t encode (raw POST)-d "raw data"

The rule: when your data might contain spaces, special characters, or non-ASCII, use --data-urlencode. Add -G for GET. Quote everything in your shell.


Found this useful? Try the URL decoder, the URL encoder, or browse all tools.

More reading

From the blog.