How to URL-decode in Go with net/url (parse, query, path)
Table of Contents
URL encoding (percent-encoding) makes URLs safe to transmit. To decode an encoded URL in Go, use the net/url package. The most common tools are url.Parse() for full URLs and url.QueryUnescape() / url.PathUnescape() for individual components.
If you want to URL encode a path, a query, or the whole URL, see URL Encode in Go post.
A typical URL consists of the following components:
scheme://host:port/path?query
Decode a full URL with url.Parse #
The url.Parse() function takes a URL string and returns a url.URL with decoded fields.
package main
import (
"fmt"
"log"
"net/url"
)
func main() {
// decode URL by url.Parse
parsedURL, err := url.Parse("https://example.com/foo+bar%21?query=ab%2Bc&query2=de%24f")
if err != nil {
log.Fatal(err)
return
}
fmt.Printf("scheme: %s\n", parsedURL.Scheme)
fmt.Printf("host: %s\n", parsedURL.Host)
fmt.Printf("path: %s\n", parsedURL.Path)
fmt.Println("query args:")
for key, values := range parsedURL.Query() {
fmt.Printf(" %s = %s\n", key, values[0])
}
}
Result:
scheme: https
host: example.com
path: /foo+bar!
query args:
query = ab+c
query2 = de$f
Decode path and query components #
Use these helpers when you only have a piece of a URL:
url.PathUnescape()for path segmentsurl.QueryUnescape()for query valuesurl.ParseQuery()to decode and parse a full query string intourl.Values
See the example to compare these functions:
package main
import (
"fmt"
"log"
"net/url"
)
func main() {
// decode path by url.PathUnescape
path := "foo+bar%21"
unescapedPath, err := url.PathUnescape(path)
if err != nil {
log.Fatal(err)
return
}
fmt.Printf("unescaped path: %s\n", unescapedPath)
// decode query by url.QueryUnescape
query := "query=ab%2Bc&query2=de%24f"
unescapedQuery, err := url.QueryUnescape(query)
if err != nil {
log.Fatal(err)
return
}
fmt.Printf("unescaped query: %s\n", unescapedQuery)
// decode query and parse by url.ParseQuery
parsedQuery, err := url.ParseQuery(query)
if err != nil {
log.Fatal(err)
return
}
fmt.Println("parsed query args:")
for key, values := range parsedQuery {
fmt.Printf(" %s = %s\n", key, values[0])
}
}
Result:
unescaped path: foo+bar!
unescaped query: query=ab+c&query2=de$f
parsed query args:
query = ab+c
query2 = de$f
Common mistakes #
❌ Using QueryUnescape on a full URL:
decoded, _ := url.QueryUnescape("https://example.com/?q=a%2Bb")
✅ Use url.Parse for full URLs and read the components:
parsed, _ := url.Parse("https://example.com/?q=a%2Bb")
value := parsed.Query().Get("q")
❌ Confusing path and query decoding:
path := url.QueryUnescape("foo+bar%21") // "+" becomes space
✅ Use PathUnescape for path segments:
path, _ := url.PathUnescape("foo+bar%21")
Best practices #
- Decode only the part you control (path vs query vs full URL).
- Use
url.Parsefor full URLs to avoid subtle encoding bugs. - Expect multiple values per query key; use
Getor iterate.
Related topics #
- URL encode in Go - percent-encode path and query
- Join URL elements in Go - safe path assembly
- HTTP client timeout in Go - robust requests
- Print HTTP request and response in Go - inspect URLs and headers
- REST client in Go - complete request examples
Tested with Go 1.25+ | Last verified: December 2025 🎉