The first time I opened the Page indexing report for a blog I run, the "Why pages aren't indexed" table had more URLs in it than the blog had posts. That looked bad. It mostly wasn't.
An http:// address that redirects, a ?utm_source= link whose canonical points at the clean URL, an RSS feed sent with a noindex header... all of those land in that table, and none of them should be in search results anyway. Google's Page indexing report documentation says the same: most sites have some unindexed pages, and that's fine as long as you understand the reason and your important pages are indexed.
The rows that need work are the ones holding a URL you actually want in search. On a blog that almost always means a post.
Sort by URL first, reason second
Open Indexing, then Pages, and click a row in "Why pages aren't indexed". The details page lists example URLs for that reason, capped at 1,000. For each one I ask a single question: would I want this exact URL as a search result?
If not, the reason Google gives barely matters. If yes, open it in URL Inspection, which shows the live status, the canonical you declared, and the canonical Google picked.
I do it in that order because the reason names are easy to misread. "Duplicate without user-selected canonical" sounds like a fault, but Google calls it working as intended when the URL it chose is the right one. "Crawled - currently not indexed" sounds final, but Google says it may index the page later and that you don't need to resubmit.
What each reason usually holds on a blog
Reason names are the ones in the report as of October 2026. The middle column is my summary of where these come from on blogs. The last column follows Google's documentation for each reason.
| Reason | Typical blog URLs | Act? |
|---|---|---|
| Page with redirect |
http:// and www variants, old slugs, the platform subdomain you used before a custom domain |
No, unless a current post URL is redirecting. Check the target is indexed. |
| Alternate page with proper canonical tag | Tracking parameters, trailing-slash and case variants that canonicalize to the clean URL | No. Google says no action is needed. |
| Duplicate without user-selected canonical | Duplicate URLs that declare no canonical | Only if Google picked the wrong URL. Then declare the canonical. |
| Duplicate, Google chose different canonical than user | A canonical pointing at a page with different content, near-identical posts, a cross-posted copy Google treats as the original | Yes. Compare both canonicals in URL Inspection. |
| URL marked 'noindex' | Feeds, search results, tag pages you chose to hide, drafts and previews | Only if a post is in the list. |
| URL blocked by robots.txt | Admin paths, internal search | Only if a post is in the list. robots.txt doesn't keep a URL out of search; noindex does. |
| Not found (404) | Deleted posts, mistyped inbound links, old URLs from a migration | 301 it if the content moved. A removed post with no replacement can stay 404. |
| Soft 404 | Empty tag pages, archive pages past the last post, an error page served with 200 | Yes. Return a real 404 or put real content there. |
| Server error (5xx), Redirect error | Outages, redirect loops, chains from more than one migration | Yes, before anything else. |
| Crawled - currently not indexed | Thin tag and archive pages, sometimes posts | Only for posts you care about. |
| Discovered - currently not indexed | New posts, deep archive pages | Usually wait. Google gives no specific fix. |
Predict the row before Google recrawls
You don't have to wait for a recrawl to know where a URL will land. The status code, the Location header, X-Robots-Tag, the canonical link and the robots meta tag decide most of it. This prints all five for one URL:
curl -sS -D - -o page.html "https://example.com/blog/my-post?utm_source=x" \
| grep -iE '^(HTTP|location|x-robots-tag)'
grep -oiE '<link[^>]*rel="?canonical[^>]*>|<meta[^>]*name="?robots[^>]*>' page.html
I ran it against the blog on JustBlogged, the platform I build, on 17 October 2026. It lives at /blog on the main domain:
| URL requested | Response | Expected row |
|---|---|---|
http://justblogged.com/blog/blog-404-page |
301 to https://
|
Page with redirect |
https://www.justblogged.com/blog/blog-404-page |
301 to the apex | Page with redirect |
/blog/blog-404-page/ |
200, canonical without the slash | Alternate page with proper canonical tag |
/blog/blog-404-page?utm_source=x |
200, canonical to the clean URL | Alternate page with proper canonical tag |
/blog/BLOG-404-PAGE |
200, canonical to lowercase | Alternate page with proper canonical tag |
/blog/rss.xml |
200, X-Robots-Tag: noindex, follow
|
URL marked 'noindex' |
/blog/search?q=dns |
200, X-Robots-Tag: noindex, follow
|
URL marked 'noindex' |
/blog/tags/dns (tag is "DNS") |
404 | Not found (404) |
The last row is one I didn't expect. Post slugs are case-insensitive and canonicalize to lowercase, but tag URLs are case-sensitive, so /blog/tags/DNS is 200 and /blog/tags/dns is 404. Any site linking to a tag with the wrong case shows up under Not found. Worth checking on whatever platform you're on, since slugs and tags are often handled by different routes.
robots.txt under /blog does nothing
This one catches a lot of blogs that live under a path. That blog serves a file at /blog/robots.txt, but Google's robots.txt documentation says crawlers only read the file at the top level of the host. Anything under /blog is governed by the root robots.txt, and a rule in the blog's own file has no effect on Google.
So the noindex header is what keeps /blog/search out. The root file doesn't block it, Googlebot fetches it, reads the header, and files it under "URL marked 'noindex'". If you block it in robots.txt instead, Google's canonicalization guide warns it can still be indexed without its content, which is the "Indexed, though blocked by robots.txt" warning.
Platform defaults matter here too. WordPress sites created on 6.4 or later have attachment pages disabled and redirect them, so they show under Page with redirect. A site upgraded from an older version keeps them on, and they can turn up as indexable URLs.
The order I fix rows that hold posts
- Server errors and redirect errors. Google can't evaluate a page it can't fetch, and an outage or loop usually hits every post at once.
- Posts under "URL marked 'noindex'". Usually a leftover staging setting, a theme option, or a per-post SEO toggle. Remove it, then inspect the live URL.
- Soft 404s. Real 404 for pages that are gone, real content for pages thin enough to look like errors.
- "Duplicate, Google chose different canonical than user". Google won't pick a declared canonical that isn't similar to the page, so check the target has the same content. Cross-posted copies on Medium, DEV or Hashnode are the usual suspect when the canonical tag on the copy is missing.
- Posts under "Crawled - currently not indexed". No fix attached. What you control is whether the post adds something your other posts don't, and whether your own posts link to it. Resubmitting it every day doesn't help.
Once every example URL under a reason is fixed, click Validate Fix on that reason. Google says validation typically takes up to about two weeks, and not to click it again until it has passed or failed.
If you moved hosts recently, most of the 404 and redirect rows are probably old URLs, and the migration redirects are the thing to fix before the report itself
Top comments (0)