Go by Example

URL Parsing

net/url.Parse to decompose URLs, url.URL fields for scheme, host, path, and query, url.Values for safe query parameter encoding.

The net/url package parses and builds URLs safely. url.Parse decomposes a raw URL string into its components; url.Values encodes query parameters without manual escaping.

url.Parse returns a *url.URL with fields for each component. All fields are strings - no type conversion needed. An error is only returned for malformed input that cannot be parsed at all; many "bad" URLs still parse without error.

package main
 
import (
    "fmt"
    "net/url"
)
 
func main() {
    u, err := url.Parse("https://api.example.com:8080/v1/users?page=2&limit=50#results")
    if err != nil {
        panic(err)
    }
 
    fmt.Println(u.Scheme)   // https
    fmt.Println(u.Host)     // api.example.com:8080
    fmt.Println(u.Path)     // /v1/users
    fmt.Println(u.RawQuery) // page=2&limit=50
    fmt.Println(u.Fragment) // results
}

url.URL.Query() parses the raw query string into a url.Values map. Use it to read individual parameters. url.Values.Encode() serialises back to a query string, sorting keys alphabetically for deterministic output.

package main
 
import (
    "fmt"
    "net/url"
)
 
func main() {
    u, _ := url.Parse("https://search.example.com?q=golang+channels&sort=date&page=1")
 
    params := u.Query()
    fmt.Println(params.Get("q"))    // golang channels
    fmt.Println(params.Get("sort")) // date
 
    // Build a new query string safely
    v := url.Values{}
    v.Set("q", "go generics <1.18")
    v.Set("sort", "relevance")
    fmt.Println(v.Encode()) // q=go+generics+%3C1.18&sort=relevance
}

u.Hostname() strips the port from Host, and u.Port() extracts just the port. Use these instead of splitting Host manually to handle IPv6 addresses correctly.

package main
 
import (
    "fmt"
    "net/url"
)
 
func main() {
    u, _ := url.Parse("https://api.example.com:8080/health")
 
    fmt.Println(u.Hostname()) // api.example.com
    fmt.Println(u.Port())     // 8080
 
    // Rebuild the URL with a different path
    u.Path = "/v2/health"
    fmt.Println(u.String()) // https://api.example.com:8080/v2/health
}

In production

Always parse user-supplied URLs with url.Parse before use - do not string-concatenate URLs or use fmt.Sprintf to build query strings. Use url.Values.Encode() to safely encode query parameters; it handles special characters correctly and sorts keys deterministically, making URLs easier to cache and compare. When constructing outbound API URLs in production code, build a url.URL struct from components rather than formatting a string: it prevents accidental double-encoding and makes the code easier to read and test. For path segments that may contain slashes or special characters, use url.PathEscape rather than url.QueryEscape - they escape different character sets.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free