DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
Go

How to Parse URLs in Go with net/url (Absolute, Relative, Query, and Request Targets)

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Go’s standard-library net/url package. Call url.Parse for ordinary absolute or relative URL references, and url.ParseRequestURI when the input is an HTTP request target. Parsing is only syntax handling: validate the scheme, host, and any other requirements your application has before using the result.

This guide shows complete code for both contexts, explains decoded versus escaped paths, strict query parsing, request-target formatting, relative-reference resolution, failure handling, and production concerns.

Choose the parser from the input context

The two commonly confused functions have different contracts. Parse accepts a URI reference, including relative references. ParseRequestURI applies HTTP request-target rules and assumes there is no fragment.

Function Use it for Important behavior
url.Parse General absolute or relative references A string such as /images/logo.svg is valid. A host and path without a scheme can be ambiguous, so parsing does not prove that the value is an absolute URL.
url.ParseRequestURI Values received as an HTTP request target, such as r.RequestURI Accepts an absolute URI or absolute path and assumes no fragment suffix.

After either call, check the returned error. Then enforce application policy explicitly. For an absolute URL, require both Scheme and Host; for a relative link, require the fields appropriate to your use case.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Parse a general URL reference

This function accepts both absolute and relative input. The validation below turns the parser into an “absolute URL only” helper:

package main

import (
	"errors"
	"fmt"
	"net/url"
)

func parseAbsolute(raw string) (*url.URL, error) {
	u, err := url.Parse(raw)
	if err != nil {
		return nil, fmt.Errorf("parse URL: %w", err)
	}
	if u.Scheme == "" || u.Host == "" {
		return nil, errors.New("expected an absolute URL with scheme and host")
	}
	return u, nil
}

func main() {
	u, err := parseAbsolute("https://example.com/docs/page?lang=en#install")
	if err != nil {
		panic(err)
	}
	fmt.Println("scheme:", u.Scheme)
	fmt.Println("host:", u.Host)
	fmt.Println("path:", u.Path)
	fmt.Println("fragment:", u.Fragment)
}

If your feature intentionally accepts relative references, omit that final policy check and handle u.IsAbs(), u.Host, and other fields according to your rules. A successful Parse call is not an authorization or safety decision.

Parse an HTTP request target

Inside an HTTP handler, parse the request target supplied by the server:

func handler(w http.ResponseWriter, r *http.Request) {
	u, err := url.ParseRequestURI(r.RequestURI)
	if err != nil {
		http.Error(w, "bad request target", http.StatusBadRequest)
		return
	}
	fmt.Fprintf(w, "path=%s query=%s", u.Path, u.RawQuery)
}

ParseRequestURI is appropriate because an HTTP request target is not necessarily an absolute URL. It accepts an absolute URI or an absolute path and rejects the fragment-style suffix that belongs to client-side navigation rather than an HTTP request target. For a value copied from a page, configuration file, or database, use Parse instead.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read URL components correctly

Decoded path versus escaped path

URL.Path is decoded. If the spelling of an escape matters, use EscapedPath():

u, err := url.Parse("https://example.com/foo%2fbar")
if err != nil {
	panic(err)
}
fmt.Println(u.Path)          // /foo/bar
fmt.Println(u.EscapedPath()) // /foo%2fbar
fmt.Println(u.String())      // https://example.com/foo%2fbar

The encoded slash (%2F) and a literal slash can have different meaning to a router, signature scheme, or object-store key. Do not compare or authorize using only Path when that distinction is significant. String() uses the escaped path representation while serializing the URL.

Host, user information, and ports

Use u.Host for the authority as written, u.Hostname() for the host without a port, and u.Port() for the port. If credentials are present, u.User exposes them; avoid logging u.String() without considering whether it could disclose a password.

Read and modify query parameters

Convenient access with Query

u, err := url.Parse("https://example.com/search?q=go&page=2")
if err != nil {
	return err
}
values := u.Query()
fmt.Println(values.Get("q"))       // go
fmt.Println(values["page"][0])     // 2

Query() returns url.Values, supports repeated keys, and is convenient when malformed pairs should not stop processing. It silently discards malformed query pairs, so it is not strict validation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Surface malformed query data with ParseQuery

values, err := url.ParseQuery(u.RawQuery)
if err != nil {
	return fmt.Errorf("invalid query: %w", err)
}
values.Set("page", "3")
u.RawQuery = values.Encode()

Use this path when clients must receive an error for malformed escaping or malformed key-value pairs. After changing values, assign the encoded result back to RawQuery. Encode produces canonical form ordering and escaping; do not assume it preserves the original byte-for-byte query spelling.

Produce the request URI

When an HTTP client or signing routine needs only the encoded path and query, call RequestURI():

u, err := url.Parse("https://example.org/path?foo=bar")
if err != nil {
	return err
}
fmt.Println(u.RequestURI()) // /path?foo=bar

The result excludes scheme, host, user information, and fragment. It is the encoded path-and-query portion used in an HTTP request. For an empty path, Go may emit / as required by request formatting.

Resolve relative references against a base

Parse the base and reference separately, then resolve according to RFC 3986 rules:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
base, err := url.Parse("https://example.com/docs/")
if err != nil {
	return err
}
ref, err := url.Parse("../guide")
if err != nil {
	return err
}
absolute := base.ResolveReference(ref)
fmt.Println(absolute.String()) // https://example.com/guide

The base must be absolute for a useful absolute result. A reference beginning with / replaces the base path, while one without a leading slash is resolved relative to the base directory. A reference containing its own scheme or host replaces those components. Treat externally supplied references as untrusted: resolution can legitimately produce a different host, so apply an allow-list before fetching.

A reusable, strict parsing helper

Centralizing policy prevents one call site from accepting relative input while another assumes an absolute network URL:

type ParsedURL struct {
	URL *url.URL
}

func ParseHTTPS(raw string) (*ParsedURL, error) {
	u, err := url.Parse(raw)
	if err != nil {
		return nil, fmt.Errorf("invalid URL syntax: %w", err)
	}
	if u.Scheme != "https" {
		return nil, fmt.Errorf("scheme must be https, got %q", u.Scheme)
	}
	if u.Host == "" {
		return nil, errors.New("URL has no host")
	}
	if u.User != nil {
		return nil, errors.New("userinfo is not accepted")
	}
	return &ParsedURL{URL: u}, nil
}

Adapt the policy to your service. You might permit http for local development, reject fragments for server-side fetches, cap URL length, or allow only specific hostnames. Those are application decisions, not guarantees made by net/url.

Common errors and fixes

  • “It parsed, but the host is empty.” You supplied a relative reference or an ambiguous host-and-path string. Require Scheme and Host when an absolute URL is mandatory.
  • “%2F disappeared.” Path is decoded. Use EscapedPath() for the original escaped representation.
  • “Bad query input was accepted.” Query() discards malformed pairs. Parse RawQuery with url.ParseQuery and handle its error.
  • “A fragment reached my server.” Fragments are client-side and are not sent in normal HTTP requests. Request-target parsing assumes no fragment; reject or remove one when your input contract is a request target.
  • “The generated URL changed ordering or escaping.” Re-encoding through url.Values.Encode() canonicalizes the query. Preserve the original raw string if byte-level fidelity is required.
  • “Relative links resolve to an unexpected domain.” Check the resolved URL’s scheme and hostname against an allow-list before making a request.

Performance, reliability, and security notes

  • net/url is in the standard library and needs no third-party dependency. Parsing is normally inexpensive, but validate input size before accepting untrusted values.
  • Always check errors; never continue with a partially trusted URL after a failed parse.
  • Do not treat syntactic validity as proof that a destination is reachable, safe, or authorized. Apply scheme, host, port, redirect, and network-policy checks separately.
  • Use escaped forms for signing and request construction when the exact wire representation matters. Use decoded fields for user-facing path operations only when that is the intended semantics.
  • Write tests for absolute URLs, relative references, repeated query keys, malformed escapes, encoded slashes, empty paths, fragments, Unicode, and userinfo.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If your goal is to obtain a clean screenshot of a parsed or resolved URL rather than operate a browser yourself, ScreenshotNeo provides a website screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP tools—take_screenshot, get_page_info, and capture_pdf—work with Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One GET request is enough:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for all options. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up free.

Equivalent calls from Python and Node.js

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Frequently Asked Questions

Does url.Parse verify that a URL is reachable?

No. It parses syntax only. DNS, TLS, redirects, authentication, and application allow-lists must be handled separately.

Can I use ParseRequestURI for every URL?

Use it for HTTP request-target input. General links and configuration values should normally use url.Parse, which supports relative references and fragments.

Which field should I store for a URL?

Store the original string when exact fidelity matters, and retain the parsed URL for structured operations. Choose Path, EscapedPath(), or RequestURI() according to the consumer’s contract.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.