URL Encoding in Go
Part of the Encoding Toolkiturl.QueryEscape(value)the right default for a query parameter value — url.PathEscape is the one for a path segment, since the two disagree on space and +
Explanation
Go's net/url package splits encoding by where the value is going — a query string or a path segment — rather than offering one general-purpose function, and the two disagree on two characters that matter.
url.QueryEscape — for a query parameter value
Follows the application/x-www-form-urlencoded convention: a space becomes +, and a literal + in the input gets escaped to %2B so it isn't misread as an encoded space on the way back. Per the standard library docs' own example, QueryEscape("my/cool+blog&about,stuff") produces "my%2Fcool%2Bblog%26about%2Cstuff" — /, +, &, and , all escaped.
url.PathEscape — for a path segment
A space becomes %20 here, not + — there's no form-encoding convention in a path. It still escapes / (a single path segment shouldn't contain a literal one), but a literal + passes through untouched, since + has no special meaning outside a query string. The same standard-library example run through PathEscape instead produces "my%2Fcool+blog&about%2Cstuff" — note the + and & survive unescaped here, where QueryEscape would have escaped both.
Using the wrong one doesn't error, it just silently produces the wrong encoding for that context: a value containing a literal +, escaped with PathEscape and then decoded somewhere that treats it as a query string, turns into a space instead of the plus sign it started as.
url.Values.Encode — sorted by key, not insertion order
Building a query string from a url.Values map (rather than key by key) goes through Encode(), whose own doc comment is explicit about this: "Encode encodes the values into 'URL encoded' form ('bar=baz&foo=quux') sorted by key." Insert b before a and the output still comes out a=...&b=... — deterministic, but not in the order you added them, which matters if you're comparing output against a fixed string in a test or computing a signature over the exact bytes.
For the underlying rules both functions implement, see URL Encoding Explained. For the same query-vs-path split expressed differently in JavaScript, see URL Encoding in JavaScript.
Valid examples
url.QueryEscape("my/cool+blog") // "my%2Fcool%2Bblog"A query value — / and + both escaped, per the standard library's own documented example.
url.PathEscape("my/cool+blog") // "my%2Fcool+blog"The same input as a path segment — / is still escaped, but + survives untouched since it has no special meaning outside a query string.
url.Values{"b": {"2"}, "a": {"1"}}.Encode() // "a=1&b=2"b was added first, but Encode() sorts by key — the output is always alphabetical, not insertion order.
Invalid examples
url.PathEscape(userInput) // used for a query parameter valueA literal + in userInput survives PathEscape unescaped, then gets silently misread as an encoded space by anything decoding it as a query string.
fmt.Sprintf("%s?q=%s", baseURL, userInput)No escaping at all — a userInput containing & or # corrupts the query string structure the moment it's not escaped.
url.QueryEscape("hello world") == url.PathEscape("hello world")false — QueryEscape produces "hello+world", PathEscape produces "hello%20world". Both are valid encodings; they just don't match character-for-character.