Home / Blog / URL HTTP & cURL

URL, HTTP & curl

REST Response Headers: Follow Pagination Links Deliberately

Read pagination links in a REST response, resolve relative targets against its URL, and prevent loops, off-origin credential forwarding, and lost items.

A REST collection can communicate pagination through a response header, a body member or another documented convention. When it uses a Link header, the client should identify the link relation that means the next page. Do not assume every API uses the same field, relation or page-number scheme.

For example, a response for /notes?page=1 might include a next link containing a cursor. That cursor is a service-defined continuation value. Replacing it with a guessed page number can skip records or change the ordering the server intended.

Read a small illustrative exchange

HTTP/1.1 200 OK
Content-Type: application/json
Link: <?cursor=after-note-7>; rel="next"

{"items":[{"id":7,"title":"Map notes"}]}

The response body contains the current page. The Link field describes a relationship to another target. The Link header reference explains its general syntax; an endpoint still needs to document what its relations and representations mean.

Use a parser that supports the header's grammar if processing arbitrary Link fields. Splitting blindly on every comma or semicolon can break quoted parameters or more complex targets. The example below starts after a proper parser has selected the next relation and extracted its target.

Resolve the target and apply a destination policy

function allowedNextPage(target, responseUrl, allowedOrigin) {
  if (typeof target !== "string" || !target.trim()) {
    throw new Error("A next-page target is required.");
  }
  const next = new URL(target, responseUrl);
  if (next.protocol !== "https:" || next.origin !== allowedOrigin ||
      next.username || next.password) {
    throw new Error("Next-page destination is outside the allowed API.");
  }
  next.hash = "";
  return next.href;
}

For response URL https://api.example.com/notes?page=1 and target ?cursor=after-note-7, this returns a URL on the same API path with the new cursor query. A target on another origin is rejected by this example's deliberate same-origin policy.

The allowedOrigin value must come from trusted configuration, not from the untrusted link being checked. Some legitimate APIs use multiple hosts; supporting them requires a documented destination and credential policy. Removing the check entirely is not equivalent to implementing that policy.

The helper removes the fragment because it is not part of the HTTP request target. This also prevents fragment-only variations from appearing to be different page requests when the application records visited URLs.

Preserve the collection's ordering and ownership

Keep the next link with the page that produced it. A newer search, filter or account selection must retire a previous pagination run so that late results cannot be appended to the wrong collection. The cursor from yesterday's query should not be reused for today's unrelated filter.

Do not assume the collection remains unchanged while pages are fetched. The service may offer a snapshot token or define weaker consistency. If duplicate identifiers appear across pages, resolve them according to the application's documented policy instead of silently deleting data solely to make counts look tidy.

Bound the traversal and handle failure

Track visited continuation URLs or tokens, and stop when a repeated continuation indicates a loop. Also set a maximum number of pages or records for the operation. A malformed service response should not cause an indefinite background crawl.

If page three fails after pages one and two succeed, label the result as partial. Do not show a completed export with a missing final portion. A retry should retain the correct continuation point and respect the endpoint's rate policy, without resending a previous mutation.

A collection with an empty current page may still have a documented next link. Base termination on the endpoint's continuation contract, not solely on the number of items in one response. Conversely, a full page without a continuation signal does not justify constructing another URL by guesswork.

Browser code also needs the Link header exposed when cross-origin response rules require it. Seeing a next link in DevTools does not prove fetch can read it. The response's exposure policy governs that separate boundary.

Use a controlled multi-page fixture

Test a relative next target, an absolute allowed target, an off-origin target and a URL containing credentials. Then simulate two pages followed by no next relation, a repeated link and a failure after a partial result. Verify the final item order and completion label.

The Response Header Checker analyzes pasted headers; it does not crawl a collection or validate this traversal policy. Use it only as a limited inspection aid while the application's pagination tests verify the actual sequence of requests and results.