Why Google Crawls Your Page but Never Indexes It

Crawled and indexed are not the same thing. Learn why a page that loads fine in your browser can sit unindexed for weeks and fixes with real code that helps to index.

You publish a page and wait a week then search for it by its exact title. Nothing. Search Console calls it "Discovered, currently not indexed" or "Crawled, currently not indexed." The page opens fine in your own browser, so it's tempting to assume Google just hasn't gotten around to it yet.

Crawling and indexing are two separate steps, and that gap is where most stuck pages live. Google's own documentation says this plainly: a page can be fetched successfully and still never enter the index, because of low content quality, a robots tag blocking it, a page design that hides the real content from the crawler, or content that doesn't match anything people search for. Below are the causes we run into most often, in roughly the order worth checking them, with the schema code to fix the last one properly.

Your content might not exist yet when the crawler looks

Modern site builders, page builders, and JavaScript-heavy themes often build the visible page in the browser after the initial HTML loads: a script runs, fetches the content, and injects the heading and body text into an empty container. A person watching the page load never notices, because it happens in under a second.

A crawler is a different story. Google renders JavaScript, but it does so in a second, separate pass after the initial crawl, and that pass can take anywhere from a few seconds to much longer depending on how busy Google's rendering queue is. Bing runs its own crawler, Bingbot, and its JavaScript rendering support is considerably more limited in practice, so a page that depends entirely on a script for its content is far more likely to reach Bing as an empty shell.

The test is simple: right-click the page and choose "View page source" (not "Inspect," which shows the DOM after scripts have run). If the heading and body text you can see on screen aren't in that raw source, a crawler that doesn't execute your JavaScript sees nothing there either. The fix depends on your platform, but the principle is the same everywhere: the words a visitor reads need to exist in the HTML the server sends, not just in what a script builds afterward. On WordPress this is rarely an issue since pages render server-side by default, but it's worth checking after installing a page builder plugin that leans heavily on client-side rendering for a specific section.

Every page might be fighting its own twin

Open ten posts on a site and check their title tags. If more than one or two read the same, or fall back to the site name with nothing else, that's a duplicate-title problem, and it's one of the clearest quality signals Google's documentation calls out directly: it advises specifying a title and description unique to each page, warns against duplicate content wasting crawl resources, and treats a generic, unhelpful title as a sign of low-effort content.

This usually isn't a writing problem. It's a configuration one: an SEO plugin installed but never filled in per post, a template that hardcodes one title for an entire section, or a custom page that was never wired up to accept its own title and meta description at all. Every individual post, product, or category needs its own title tag and meta description, pulled from that item's own content rather than a single site-wide default.

If you're on WordPress, Yoast SEO or Rank Math will generate these per post automatically once you fill in the fields; the mistake is leaving them blank and assuming the plugin invents something useful on its own. If you're running a custom-built site, check that the template receives the current page's title and description as data, rather than a constant defined once for the whole app.

Structured data: what it is and what it changes

Structured data, usually written as JSON-LD, is a block of machine-readable facts about your page: what kind of thing it is, who wrote it, when it was published, what an organization is called. It doesn't directly boost rankings, but it does two things that matter. It makes a page eligible for extra features in search results (star ratings, article carousels, FAQ dropdowns), and it gives search engines and AI answer tools a clean, unambiguous way to read the same facts a person would otherwise have to infer from the page's prose.

Here are the three types worth adding first, since they cover most sites.

Organization: who you are

This one goes on every page, usually generated once in a shared template rather than written by hand per page. It tells search engines your business name, official URL, and logo, so they can resolve "your brand" to one consistent entity instead of guessing from scattered mentions.

{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "Your Company Name",
  "url": "https://yoursite.com",
  "logo": "https://yoursite.com/logo.png",
  "sameAs": [
    "https://www.facebook.com/yourcompany",
    "https://twitter.com/yourcompany"
  ]
}

The sameAs array is optional, and it's just a list of your official profiles elsewhere on the web. Leave it out if you'd rather not maintain it, but it's a cheap way to tie your social presence to the same entity.

Article or BlogPosting: what this specific page is

Every blog post or article gets its own copy of this, filled in from that post's actual data: its real title, its real publish date, its real featured image. Copying the same block across every post with only the headline changed defeats the purpose, since datePublished and dateModified need to reflect that post specifically.

{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Your post's actual title",
  "description": "A one or two sentence summary of the post.",
  "image": ["https://yoursite.com/images/featured.jpg"],
  "datePublished": "2026-01-15T09:00:00+00:00",
  "dateModified": "2026-01-20T14:30:00+00:00",
  "author": {
    "@type": "Organization",
    "name": "Your Company Name"
  },
  "publisher": {
    "@type": "Organization",
    "name": "Your Company Name",
    "logo": {
      "@type": "ImageObject",
      "url": "https://yoursite.com/logo.png"
    }
  },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://yoursite.com/blog/your-post-slug/"
  }
}

BreadcrumbList: where this page sits

This spells out the path from your homepage down to the current page, matching the breadcrumb trail a visitor would click through. It's one of the more common pieces search results show directly under a title.

 

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Blog",
      "item": "https://yoursite.com/blog/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Your post's actual title",
      "item": "https://yoursite.com/blog/your-post-slug/"
    }
  ]
}

 

Validate what shipped, not what you wrote

If you're on WordPress, an SEO plugin already generates all three of these for you the moment you fill in a title, excerpt, and featured image; there's rarely a reason to hand-write JSON-LD on top of it. If you're maintaining a custom-built site, or pasting this into a template engine of your own, check the page's rendered HTML after you add it, not just the code you typed. JSON-LD blocks are easy to get syntactically right and semantically broken by whatever renders them: template engines that use their own special characters (Blade's @-prefixed directives, Handlebars' or Vue's double curly braces) can silently rewrite part of a hand-written JSON-LD block before it ever reaches the browser, without throwing an error anywhere you'd notice. Google's Rich Results Test and the independent Schema Markup Validator both parse the JSON-LD your page served and will tell you immediately if something in it isn't valid.

A few more common blockers, in brief

Problem What to check
Accidental noindex View page source and search for <meta name="robots">. If it says noindex on a page you want found, that's your answer. This happens most often on a "staging" setting left switched on after launch.
robots.txt blocking the page Check yoursite.com/robots.txt for a Disallow line matching the page's path. If a crawler can't fetch it at all, a noindex meta tag on it won't even be read.
Fabricated freshness If your sitemap's lastmod date changes on every single page every day regardless of whether anything changed, that's a signal search engines have learned to distrust, and it can drag down trust in the rest of the sitemap too.
No sitemap submitted A new site with no sitemap in Search Console or Bing Webmaster Tools is relying entirely on internal links to be discovered. Submitting one doesn't guarantee indexing, but it removes discovery as a variable.

Where this leaves you

None of this is a ranking trick. It's making sure the page you built is the page a crawler receives: real content in the raw HTML, a title and description that belong to that page alone, and structured data that describes what's really there. Page speed plays into the same story, since a server that's slow to respond delays every one of these checks and can push a page further back in the rendering queue. That's one of the reasons SSD-backed hosting with a free SSL certificate, both included on Lvato's shared hosting plans, is worth having even before you touch a single SEO setting: the content still has to load fast enough and securely enough for a crawler to bother finishing the job.

Next in this series: the structured data types beyond these three, including FAQPage and product markup, and what each one unlocks in search results.