Pagination Without rel=next and rel=prev
rel="next" and rel="prev" were link elements that declared a paginated sequence. Google announced some years ago that it had not been using them for indexing for a long while, and the announcement is the reason most pagination advice written before it is wrong. Other engines and user agents may still read them, so emitting them is harmless, but they are not doing the job people believe.
What replaced them is unglamorous: ordinary crawlable links between pages, a self-referencing canonical on each page, and nothing clever.
The three patterns that break
Canonical every page to page one. The most damaging and still the most common. Page 2 of a listing declares page 1 as its canonical, which asserts that the two are the same page. They are not — they contain different items. The likely outcomes are that the canonical is ignored, in which case you achieved nothing, or that it is honoured, in which case the items on pages 2 onward are only discoverable if something else links to them.
If the paginated listing is your primary path to older content, that second outcome orphans it. The mechanism is the one in internal linking as plumbing: a page reachable only through a crawl path that no longer exists is reachable only from your sitemap.
noindex on page two onwards. Keeps the pages out of results, which was the goal, and severs nothing if the links are still followed — a noindexed page’s links are followed unless you also set nofollow. So this is survivable. But it is unnecessary, and the frequent variant noindex, nofollow is not survivable, for the reason above. The difference is in noindex and nofollow on the same page.
A “view all” page as the canonical target for the sequence. This was documented practice once and is no longer. If a view-all page exists and is genuinely usable, it can be indexed on its own merits; canonicalising a paginated sequence to it asserts a duplicate relationship that does not hold. And on a large listing, a view-all page is often too slow to be worth serving at all.
What to do instead
Each paginated page canonicals to itself. /blogs/page/3/ declares /blogs/page/3/ as its canonical, with the page parameter or path segment intact. That is an accurate description of reality: it is a distinct page with distinct content.
Each page links to the next and previous with real anchors. An <a href> a crawler can follow. Not a button, not a JavaScript handler, not an infinite scroll with no underlying links.
Page one is the clean URL, without a page indicator. /blogs/ rather than /blogs/page/1/. If both exist, the numbered form should 301 to the clean form — otherwise you have a duplicate of your most important listing page.
Every item on a paginated page links directly to the item. This is what pagination is for, from a crawl perspective: it is a discovery surface for the items. If the items are reachable, the paginated pages have done their job whether or not they rank.
That is the entire specification. It is boring, which is the point.
Titles and descriptions on paginated pages
Paginated pages will look like near-duplicates unless the title and description differ, and the cheapest honest differentiation is to include the page number:
<title>Archive — Page 3 | Example</title>
<meta name="description" content="Page 3 of the Example archive.">
Do not write unique prose descriptions for page 47. Do not add introductory copy to every paginated page to make it “substantial” — that produces repeated boilerplate, which is worse than a thin page. The page number in the title is sufficient and truthful.
If a paginated page has no items at all — someone requested page 200 of a 12-page listing — it should return a 404 rather than an empty listing with a 200, which is a soft 404 by construction. Bound-check the page parameter.
Infinite scroll and “load more”
Both replace pagination links with JavaScript-driven appends, and both remove the crawl path unless you keep one underneath.
The workable pattern is progressive enhancement, in this order:
- Build paginated URLs that work without JavaScript.
/blogs/page/2/returns a full page of items. - Render real
<a href>pagination links in the HTML. - Layer the scroll or load-more behaviour on top, intercepting the links.
The result is a page that works for a crawler and for a user with a script blocker, and behaves as an infinite scroll for everyone else. The alternative — a container that is empty until a fetch resolves — is a page whose items may not be discovered at all.
history.pushState is worth using so that the URL updates as a user scrolls, which makes the state shareable. It does not affect discovery, since the crawler is reading the links rather than simulating scrolling.
Paginated URLs as parameters versus paths
?page=3 and /page/3/ are equally valid. Two practical differences:
- The parameter form participates in whatever parameter policy you have. If you have a broad
Disallow: /*?rule to control facets, it will catch your pagination too, which silently blocks the discovery surface. Check for this — it is a real and easily-missed interaction with the patterns in faceted navigation and the crawl space it opens. - The path form composes badly with facets, since you end up with
/laptops/gaming/page/3/and have to decide where the page segment sits relative to the facet segments.
Pick whichever your platform emits naturally and make sure your robots rules and canonical logic account for it.
Pagination through a migration
Paginated URLs are the least stable URLs on a site: the item on page 3 today is on page 5 next month. That has a specific consequence for redirects — there is no meaningful one-to-one mapping between old and new paginated URLs after a replatform, because the contents have moved.
The honest approach is to redirect page one to page one, redirect numbered pages to the equivalent numbered page if the listing still exists, and accept that the mapping is approximate. What must not happen is paginated URLs 404ing en masse, because they are frequently a substantial share of the crawl surface. The sequencing for the rest of it is in preserving links through a site migration.
The check
# does page two exist as a followable link from page one?
curl -s https://example.com/blogs/ \
| grep -oE 'href="[^"]*page[^"]*"' | sort -u
# does page two canonical to itself?
curl -s https://example.com/blogs/page/2/ \
| grep -o '<link rel="canonical"[^>]*>'
Two conditions, and both have to hold. A canonical on page 2 that names page 1 is the bug this post exists to name; an absence of followable pagination links is the bug that hides it.