How Query Strings Work in URLs
Most URLs you interact with daily contain query strings. Understanding exactly how they're structured — and what can go wrong when they're assembled carelessly — is essential for anyone working with web APIs, tracking links, or redirect flows.
Query string anatomy
A query string begins with ? after the path and contains key-value pairs separated by &:
https://example.com/search?q=json+formatter&page=2&sort=relevance
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
query string
The fragment (hash) comes after the query string: ?q=test#results. The fragment is never sent to the server — browsers use it for in-page navigation only.
Encoding and special characters
Query strings can safely contain ASCII letters, digits, and -_.. Everything else requires percent-encoding. A space becomes %20 (or + in query strings specifically). An & inside a value becomes %26. An = inside a value becomes %3D.
Forgetting this is a frequent source of broken tracking links:
# Broken — the & inside utm_campaign splits into a new parameter
?utm_campaign=summer&sale&utm_source=email
# Parser sees: utm_campaign=summer, sale=(empty), utm_source=email
# Correct — the & is encoded
?utm_campaign=summer%26sale&utm_source=email
# Parser sees: utm_campaign=summer&sale, utm_source=email
Repeated keys
The same key can appear multiple times: ?color=red&color=blue. Whether this creates an array or uses the last value depends on the server framework. PHP expects color[] for arrays. Express.js and many others return arrays for repeated keys. If you're sending multi-value parameters, check the API documentation for the expected format.
URL structure summary
https://api.example.com:443/v2/users?page=1&limit=20#top
└─┬─┘ └──────┬──────┘└─┬┘└────┬───┘└────────┬─────┘└──┬─┘
scheme host port path query fragment
urllib.parse.urlencode() in Python, new URLSearchParams() in JavaScript, URI.escape() in Ruby. Hand-concatenation is reliable only for the simplest cases.