URLs, Path and Query Parameters
A URL is the address of a resource, and its parameters are inputs written into that address. In https://shop.example.com/stores/riyadh-01/products?q=green%20tea&page=2, the path parameter riyadh-01 picks the store, and the query parameters q and page set the search term and the page of results. %20 is an encoded space, because a URL cannot contain a literal one.
The lesson on HTTP methods paired every request with a target. This lesson opens that target: the parts of a URL in RFC 3986, path and query parameters, encoding, and how to test each parameter.
Why it matters
Parameters are input fields that the interface hides. Search terms, IDs, filters and page numbers travel in the URL, so they need the same test design as a form field. Handled badly, a search for "tea & coffee" returns the wrong products, a filter is silently ignored, or changing /orders/1001 to /orders/1002 shows another customer's order: Broken Object Level Authorization, the first risk in the OWASP API Security Top 10 2023.
Editing a URL is also the fastest way to try an input or reproduce a report, and the exact encoded URL is the most precise evidence a bug report can carry.
The five parts of a URL
RFC 3986 defines the generic syntax of a URI in five parts: scheme://authority/path?query#fragment. Each part ends at a fixed delimiter, so you can split any URL by hand; the WHATWG URL Standard, which browsers follow, splits it the same way.
The client uses the scheme and authority to connect, sends the path and query as the request target, and puts the host in the Host header. The fragment never leaves the browser, so no server log or API test sees it.
- Scheme:
httpsorhttp; it also sets the default port, 443 or 80. - Authority: the host, such as
shop.example.com, and an optional:port. Scheme and host are not case-sensitive. - Path:
/stores/riyadh-01/products, segments separated by/that identify the resource. - Query: everything after
?up to#. - Fragment: everything after
#, such as#reviews: a position in the page or a route inside a single-page app.
riyadh-01 picks the store, q and page refine the list, and the #reviews fragment never reaches the server.The URL in the address bar
Part 1:https://
The request line the server receives
GET
The Host header
Host:
- 3–6Request target: the path and the query
- 5–6Query:
key=valuepairs joined by&
- 1SchemeSets the default port, 443 for
https, so no:portis written - 2Authority (host)Used to connect, then sent in the
Hostheader; not case-sensitive - 3Fixed path segmentsThe same for every store in the template
/stores/{storeId}/products - 4Path parameter
storeIdPicks the store, so it is required; an unknown store should get404required - 5Query parameter
qThe search term, percent-encoded; the server decodes it to:green tea - 6Query parameter
pageUsually optional with a default, so leaving it out is a test of its ownoptional - 7FragmentHandled by the browser only, so it is missing from the request linenot sent
Text version
https://shop.example.com/stores/riyadh-01/products?q=green%20tea&page=2#reviews
scheme https
authority shop.example.com (no port: 443, the https default)
path /stores/riyadh-01/products
query q=green%20tea&page=2
fragment reviews (stays in the browser)
What the server receives:
GET /stores/riyadh-01/products?q=green%20tea&page=2 HTTP/1.1
Host: shop.example.comPath parameters: which resource
RFC 3986 only knows path segments. "Path parameter" is the API-design name for a segment whose value changes per request: the documentation writes a template, /stores/{storeId}/products, and each request fills it in. OpenAPI marks it in: path; websites use the same idea for slugs like /products/green-tea-500g.
A path parameter identifies the resource, so it is required: /stores//products reaches another route or none. A wrong type, such as /orders/abc for a numeric ID, should get a 4xx client error, not a 500; an unknown ID should get 404; and another user's ID must never return that user's data. An encoded slash %2F in the value is handled differently by different servers, so ask what is intended and test it.
Query parameters: how to read the resource
The query refines the request: search, filter, sort, paginate. RFC 3986 only defines where it starts and ends; the key=value pairs joined by & come from the HTML form format application/x-www-form-urlencoded, defined in the WHATWG URL Standard and followed by most frameworks. Query parameters are usually optional with defaults, so an absent parameter is behaviour too. Each of these is a different input:
?page=2: a key with a value.?page=or?page: present but empty, not the same as leavingpageout.?tag=tea&tag=gifts: a repeated key. No standard says which value wins; frameworks take the first, the last or all of them.?Page=2: many frameworks compare keys case-sensitively, so this may be an unknown key that is silently ignored.
Percent-encoding: keeping values intact
RFC 3986 reserves the characters that structure a URL: : / ? # [ ] @ separate the parts, and ! $ & ' ( ) * + , ; = separate data inside them. The letters A to Z and a to z, the digits 0 to 9 and - . _ ~ never need encoding. Every other character in a value, including a space, should be percent-encoded: each byte becomes % and two hexadecimal digits.
Characters outside ASCII, the basic set of 128 Latin letters, digits, symbols and control codes, are first turned into bytes with UTF-8, the character encoding of the web: an ASCII character takes one byte, any other character two to four. The Arabic word قهوة (coffee) is four letters of two bytes each, so it is sent as eight encoded bytes: %D9%82%D9%87%D9%88%D8%A9. Encode each value, never the whole URL, or / and ? turn into %2F and %3F and the URL breaks.
+means a space only in form-encoded data, which is how most servers read a query; in a path it is a plus sign. Encode a real plus as%2B, or+966…arrives as a space followed by966….- Encode once and decode once:
%2520is a space encoded twice, and a server that decodes twice may let an encoded character slip past its input validation.
Value Encoded query value
green tea green%20tea
tea & coffee tea%20%26%20coffee
50% 50%25
#1 seller %231%20seller
+966500000000 %2B966500000000
قهوة %D9%82%D9%87%D9%88%D8%A9Worked example: testing a product list URL
Requirement: GET /api/v1/stores/{storeId}/products lists a store's products. q is an optional search term; sort is price_asc (the default) or price_desc; page is an integer from 1, default 1; limit is an integer from 1 to 50, default 20. An unknown store gets 404; an invalid parameter value gets 400 with a message naming the parameter.
Two test design techniques do most of the work, and each has its own lesson that covers it in depth: equivalence partitioning splits a parameter's values into partitions, groups that the system should treat the same way, such as valid and invalid limit values, and tests one value from each; boundary value analysis then tests the value at each edge of a partition and its nearest neighbour across that edge, such as limit=50 and limit=51. Apply both to each parameter, then add the URL cases: missing, empty, repeated, unknown and encoded values. Expected results:
- No query:
200, page 1, at most 20 products, cheapest first. - Unknown store
riyadh-99:404, not an empty list or a500. limit=50is accepted;limit=51gets400with a message naminglimit. Addlimit=1andlimit=0for the lower boundary.page=0,page=abcandsort=newest:400, never a500or an undocumented fallback.- The searches must decode to "tea & coffee" and قهوة and return matching products. Repeat them in the user interface and check the DevTools Network tab: a page that joins strings sends
q=tea%20&%20coffee, and the server readsqas "tea " plus a stray key. - Repeated
pageand unknowndebug: the expected result must come from the documentation; if nothing is written, ask.
GET /api/v1/stores/riyadh-01/products
GET /api/v1/stores/riyadh-99/products
GET /api/v1/stores/riyadh-01/products?limit=50
GET /api/v1/stores/riyadh-01/products?limit=51
GET /api/v1/stores/riyadh-01/products?page=0
GET /api/v1/stores/riyadh-01/products?page=abc
GET /api/v1/stores/riyadh-01/products?sort=newest
GET /api/v1/stores/riyadh-01/products?q=tea%20%26%20coffee
GET /api/v1/stores/riyadh-01/products?q=%D9%82%D9%87%D9%88%D8%A9
GET /api/v1/stores/riyadh-01/products?page=2&page=3
GET /api/v1/stores/riyadh-01/products?debug=trueTry it: API Query Parameter Builder Try it: Special Character Dataset Generator
Common mistakes
- Building URLs by joining strings, so a value containing
&,#,+or%breaks the request or changes its meaning. - Encoding the whole URL instead of each value, or encoding a value twice.
- Testing only valid values, never missing, empty, wrong-type, boundary, repeated or unknown ones.
- Expecting the server to see the fragment: everything after
#stays in the browser. - Accepting tokens, passwords or personal data in a query string: URLs end up in server and proxy logs and in browser history, and RFC 9110 warns against putting sensitive information in them.
- Trusting a path parameter because the interface never shows other IDs, instead of checking that the server refuses another user's resource.
The QA perspective
When a requirement or API reference introduces a parameter, ask: is it required, what is its default, which values and limits are allowed, is it case-sensitive, and what happens when it is missing, empty, repeated or unknown? Each answer becomes an expected result; each "nobody knows" becomes a question before coding starts.
In test cases and bug reports, record the full URL as sent, copied from the Network tab or API client rather than retyped, plus the decoded value of any parameter that matters. Swapping a path parameter to reach another user's data is authorised testing only: use your own test accounts in an environment you may test, and report a leak through the team's security process.
Practice exercise
Read and test an order list URL
A store manager says the order list looks wrong and sends the URL below. Above it is the endpoint's reference: the endpoint returns one store's orders to that store's signed-in manager.
Work on paper, then check your answers with the URL Parser and the URL Encoder.
GET /stores/{storeId}/orders
storeId path required example: riyadh-01
status query optional new | paid | shipped default: all
customer query optional free text
page query optional integer >= 1 default: 1
errors 400 for an invalid parameter value, 404 for an unknown storeId
https://admin.example.com/stores/riyadh-01/orders?status=paid&page=0#list- Split the URL into its five parts; which one never reaches the server?
- Name the parameters and check each value against the reference.
- Encode a
customerfilter for "Al Noor & Sons". - List eight tests with expected results.
Show the model answer
Scheme https; authority admin.example.com (default port 443); path /stores/riyadh-01/orders; query status=paid&page=0; fragment list, which stays in the browser.
Path parameter storeId = riyadh-01; query parameters status = paid and page = 0, which is below the minimum of 1: expect 400. If the list loaded anyway, that may be the reported defect.
customer=Al%20Noor%20%26%20Sons (form encoding also accepts Al+Noor+%26+Sons). Unencoded, the & ends the value, and the server reads customer as "Al Noor " plus a stray key " Sons".
Tests: (1) no query: all statuses, page 1. (2) each allowed status: only matching orders. (3) status=cancelled: 400. (4) page=-1, page=abc: 400. (5) page=1 and the last page: orders. (6) encoded customer with &, %, + and Arabic letters: whole-name match. (7) unknown store riyadh-99: 404. (8) another test account's store: 403 or 404, no order data. Ask what a repeated status should do.
Frequently asked questions
What is the difference between a URL and a URI?
A URI identifies a resource. RFC 3986 calls a URI a URL when it also says how to locate the resource, for example with the https scheme and a host. Both use the same generic syntax, and in web testing the two words are used almost interchangeably.
Is + in a URL a space or a plus sign?
It depends on where it is. In a query read as form data (application/x-www-form-urlencoded), + is a space; in the path it is a plus sign. Write a real plus as %2B and a space as %20, and the value means the same everywhere.
Are URLs case-sensitive?
The scheme and host are not: HTTPS://Shop.Example.com and https://shop.example.com are the same. RFC 3986 treats the other parts as case-sensitive, but each server decides how it matches them, so /Products may work, redirect or return 404. Test it and record the behaviour the team chooses.
How long can a URL be?
No standard sets one maximum. RFC 9110 recommends that senders and recipients support URIs of at least 8000 octets, and browsers, servers and proxies each apply their own limits. A server that refuses a URL for its length should answer 414 URI Too Long. Ask for your system's limit and test just below and above it.
References
- RFC 3986: Uniform Resource Identifier (URI): Generic SyntaxThe parts of a URI (scheme, authority, path, query, fragment), reserved characters and percent-encoding.
- URL StandardHow browsers actually parse, serialize and encode URLs, including application/x-www-form-urlencoded.
- RFC 9110: HTTP SemanticsThe meaning of HTTP methods, header fields, status codes and content negotiation, independent of the wire version.
- OWASP API Security Top 10 (2023)The ten most common API weaknesses: broken object-level authorization, broken authentication, unrestricted resource consumption and more.
Practise with Mutqan tools
Break a URL into protocol, host, port, path, query parameters, fragment and credentials.
Parse and build query strings, including repeated keys and bracket-array notation.
Percent-encode text for safe use in URLs, query strings and form data.
Decode percent-encoded URLs and query strings back to readable text.
Build correctly encoded query strings for API calls with pagination, filtering and sorting parameters.
Generate inputs full of special characters: quotes, SQL/HTML/JSON metacharacters, control chars, emoji and path tricks.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.