<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 任宏涛</title>
    <description>The latest articles on DEV Community by 任宏涛 (@_6614b04c8831704fcf9b2).</description>
    <link>https://dev.to/_6614b04c8831704fcf9b2</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4107161%2Fb7e8a950-cd58-402f-8301-127c92c91752.png</url>
      <title>DEV Community: 任宏涛</title>
      <link>https://dev.to/_6614b04c8831704fcf9b2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_6614b04c8831704fcf9b2"/>
    <language>en</language>
    <item>
      <title>How AI Search Engines Choose What to Cite</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/how-ai-search-engines-choose-what-to-cite-2id1</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/how-ai-search-engines-choose-what-to-cite-2id1</guid>
      <description>&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;An AI search engine cites a page when three things are true at once: the page can be found and read by the system doing the answering, the page contains a fact stated plainly enough to be quoted, and something independent suggests the page is worth trusting. Miss any one of the three and the page is invisible to the answer, however good its content is.&lt;/p&gt;

&lt;p&gt;That is the whole mechanism in one sentence, and it is worth holding on to, because most advice about "optimising for AI search" is really advice about one of these three conditions dressed up as something new. There is no separate secret ranking for AI answers. There is a retrieval step, a reading step, and a trust step, and each of them is mundane.&lt;/p&gt;

&lt;p&gt;One more thing to say up front: no major AI company has published the exact formula its system uses to choose citations. Anyone who quotes precise weights, or a list of confirmed ranking factors for AI answers, is guessing. What follows is the behaviour that is observable from the outside — what the systems demonstrably do, and what site owners can verify for themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, the page has to be found and read
&lt;/h2&gt;

&lt;p&gt;An AI answer is built from pages the system retrieved. Retrieval needs the page to be crawlable: a live URL, a 200 status, no robots rule blocking the AI crawler, and content present in the served HTML rather than injected only by JavaScript after load. A page that renders beautifully in a browser but ships an empty shell to a crawler has given the retrieval step nothing to work with.&lt;/p&gt;

&lt;p&gt;This is the same crawlability that traditional search needs, and the checks are the same. Fetch the URL with curl and read what comes back. If the body is an empty container and a bundle of script tags, a retrieval system may never see the words you wrote. If your robots.txt blocks the AI crawlers by name, you have told them not to read the page at all — and several sites do this deliberately, because being cited is a choice, not an obligation.&lt;/p&gt;

&lt;p&gt;The llms.txt file sits in this same step. It is a plain-text index, published at the root of a site, that lists the pages the site considers important, each with a one-line description. It is a proposed convention, not a standard, and no major AI company has formally committed to reading it. Treat it as cheap insurance: it costs minutes to publish, and any system that does look gets a curated map instead of having to guess which pages matter. Our own survey of 78 well-known sites found 22 serving one, which tells you adoption is real but far from universal — see the &lt;a href="https://www.serpprism.com/guides/llms-txt-in-the-wild" rel="noopener noreferrer"&gt;llms.txt survey&lt;/a&gt; for the numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second, the answer has to be quotable
&lt;/h2&gt;

&lt;p&gt;Retrieval finds candidate pages; the system then has to extract an answer from them. The pages that get cited are usually the ones where the fact is stated in a form that can be lifted out intact: a short paragraph that answers one question, a definition in a single sentence, a table with labelled rows, a list of steps in order.&lt;/p&gt;

&lt;p&gt;This is why the structure of a page matters more for AI citation than its length. A 3,000-word essay that never states its conclusion plainly is harder to quote than a 600-word page that opens with the answer and then explains it. The first paragraph of this guide is written that way on purpose: it is the answer, stated before the explanation, in language that survives being lifted out of context.&lt;/p&gt;

&lt;p&gt;Practical consequences follow. Put the conclusion first, before the background. State numbers and dates as plain text, not only inside images or charts. Use headings that describe what the section answers, so a system scanning the page can tell which part is relevant. And avoid burying the key fact in the middle of a long paragraph where it cannot be extracted without the sentences around it. None of this is new writing advice — it is the same clarity that helps human readers — but AI retrieval rewards it more directly, because a machine cannot skim the way a person can.&lt;/p&gt;

&lt;h2&gt;
  
  
  Third, someone else has to vouch for the page
&lt;/h2&gt;

&lt;p&gt;The hardest condition to fake, and the one most often skipped, is independent corroboration. A claim that appears on one site and nowhere else is a claim, not a fact. Systems that answer questions lean on pages that are linked to, listed in directories, mentioned in roundups, or cited by other pages on the same topic.&lt;/p&gt;

&lt;p&gt;That is why the familiar signals still matter: links from real pages, listings in relevant directories, mentions in articles that are themselves trusted. They are not a separate "AI SEO" tactic. They are the same authority signals search engines have used for decades, doing the same job: telling the system that other people found this page worth pointing at.&lt;/p&gt;

&lt;p&gt;It also explains why a brand-new page from an unknown site is rarely cited on its first day, even when its content is correct. The page has no history of being pointed at, so the system has no independent reason to prefer it over ten other pages saying the same thing. Time, links, and mentions are the slow part of this, and there is no shortcut that survives contact with a system designed to distrust shortcuts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like on SerpPrism
&lt;/h2&gt;

&lt;p&gt;This site is built around the three conditions above, and it is a useful worked example because every part of it is verifiable. Each tool page states plainly, in its first paragraph, that the tool is free, needs no sign-up, and runs entirely in the browser — three facts a system answering "is there a free tool that does X" needs in quotable form. The site publishes an &lt;a href="https://www.serpprism.com/llms.txt" rel="noopener noreferrer"&gt;llms.txt file&lt;/a&gt; at its root listing the tools and guides with one-line descriptions, so any crawler that reads the convention finds a curated map. And the guides library, including our &lt;a href="https://www.serpprism.com/guides/ai-crawlers-in-the-wild" rel="noopener noreferrer"&gt;AI crawler survey&lt;/a&gt;, publishes dated measurements with the method and the raw counts, so a claim on this site can be checked rather than taken on trust.&lt;/p&gt;

&lt;p&gt;Note what this example does not claim. We do not claim any AI system has cited this site, because we have not verified that it has. We do not quote a citation rate, a share of AI answers, or a survey of what AI systems prefer, because we have not measured any of those. The example is about the structure being in place, not about a result we cannot evidence.&lt;/p&gt;

&lt;p&gt;That distinction is the point of this guide. The work is to make a page findable, quotable, and corroborated. Whether a particular system cites it on a particular day is its decision, and any report that tells you otherwise is selling certainty it does not have.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does not help
&lt;/h2&gt;

&lt;p&gt;Stuffing pages with the exact phrasing of popular questions does not help, because retrieval is not keyword matching and the system reads the page, not the query log. Adding a block of hidden text, white-on-white keywords, or an invisible FAQ does not help either, for the same reason it never helped in traditional search: the content is judged on what is visible and coherent, and hidden text is a spam signal wherever it is found.&lt;/p&gt;

&lt;p&gt;Publishing an llms.txt file and changing nothing else does not help much either. The file is a map, not a destination. If the pages it points at are thin, unquotable, or uncorroborated, the map only helps a system find them faster and dismiss them faster.&lt;/p&gt;

&lt;p&gt;Finally, beware any tool or service that promises placement in AI answers. Citation is not an inventory you can buy, and the systems that answer questions have no advertising slot for it. A service that charges for "AI visibility" is charging for work you could do yourself — writing clearly, structuring pages so answers can be extracted, and earning mentions — with a guarantee attached that nobody can honour.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do on your own site
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Check crawlability first.&lt;/strong&gt; Fetch your key pages and confirm they return 200 with the content in the HTML. Check whether your robots.txt blocks AI crawlers, and decide deliberately: blocking is a valid choice, but it should be a choice, not an accident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rewrite the opening of each important page so it answers its own question.&lt;/strong&gt; One paragraph, plainly stated, before the background. If you cannot write that paragraph, the page does not yet know what it is about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Publish an llms.txt file.&lt;/strong&gt; List the pages that represent what the site does, each with one line saying what it is for. Fifteen useful entries beat two hundred exhaustive ones. The &lt;a href="https://www.serpprism.com/tools/llms-txt-generator" rel="noopener noreferrer"&gt;llms.txt generator&lt;/a&gt; on this site writes the format for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Earn the corroboration.&lt;/strong&gt; Get the page listed where people in your field actually look, and linked from pages that already get read. There is no faster honest version of this step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Re-check after every redesign.&lt;/strong&gt; A new template that moves content behind JavaScript, or a robots.txt edit that blocks a crawler group, can silently undo all four steps above. The failure is invisible in a browser, which is why it has to be checked rather than assumed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;Everything here is inferred from observable behaviour, not from a published specification, because no such specification exists. AI systems change how they retrieve and cite without announcement, so a tactic that works this year may not work next year — the three conditions are structural, which is why they are the safest thing to build on, but they are not a promise.&lt;/p&gt;

&lt;p&gt;There is also a measurement gap worth naming. We can verify that our own pages are crawlable, quotable, and listed, and we can verify that other sites publish llms.txt files. We cannot verify, from the outside, which pages a given AI system retrieved for a given question, because that retrieval is not observable. Any guide that reports such numbers without explaining how they were measured is reporting something it cannot know.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>ai</category>
      <category>llm</category>
      <category>marketing</category>
    </item>
    <item>
      <title>How Long Should a URL Slug Be?</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/how-long-should-a-url-slug-be-2j9p</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/how-long-should-a-url-slug-be-2j9p</guid>
      <description>&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;Keep the slug — the part of the URL after the domain — short, lowercase, hyphenated, and carrying the words someone would use to describe the page. Three to five words is the working range for most pages. Anything longer is usually a sentence that escaped into the address bar, and anything shorter usually says nothing at all.&lt;/p&gt;

&lt;p&gt;There is no official limit, and no ranking bonus for hitting a particular character count. The reasons to keep slugs short are practical: people can read them, remember them, paste them without breaking them, and tell what the page is about from the URL alone. A slug is a label, and labels work best when they are short.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a slug is, precisely
&lt;/h2&gt;

&lt;p&gt;For a URL like &lt;code&gt;https://example.com/guides/how-long-should-a-url-slug-be&lt;/code&gt;, the slug is &lt;code&gt;how-long-should-a-url-slug-be&lt;/code&gt; — everything after the last slash that identifies this one page. It is distinct from the domain, which identifies the site, and from any folders before it, which identify the section.&lt;/p&gt;

&lt;p&gt;Search engines display the slug in results, browsers show it in the address bar, and people copy it into emails, documents, and chat messages. It is one of the few parts of a page that is seen by readers, crawlers, and linkers alike, which is why a small amount of care here pays off in three places at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  The working rule
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Three to five words, and never more than you need.&lt;/strong&gt; A guide about meta descriptions can live at &lt;code&gt;/meta-description-length&lt;/code&gt;. A guide about robots.txt mistakes can live at &lt;code&gt;/robots-txt-mistakes&lt;/code&gt;. Both slugs say exactly what the page covers, and neither wastes words. If your slug needs a sixth word to be accurate, take the sixth word — the rule is a guide, not a limit — but ask first whether one of the first five is doing no work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lowercase only.&lt;/strong&gt; URLs are case-sensitive after the domain, so &lt;code&gt;/Guide&lt;/code&gt; and &lt;code&gt;/guide&lt;/code&gt; are two different addresses. Mixed case creates duplicates that are invisible in a browser and confusing to everyone who links to the page. Pick lowercase once and never vary it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hyphens between words, never underscores or spaces.&lt;/strong&gt; Search engines treat a hyphen as a word separator and an underscore as a joiner, so &lt;code&gt;url-slug&lt;/code&gt; reads as two words and &lt;code&gt;url_slug&lt;/code&gt; reads as one. Spaces are worse: they get encoded into unreadable percent sequences. Hyphens are the only separator that is readable by people and machines alike.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Words a person would use.&lt;/strong&gt; The slug should contain the topic words of the page — the words someone would type to find it, and the words a reader would expect to see. This is not keyword stuffing; it is labelling. A slug of &lt;code&gt;/page-47&lt;/code&gt; tells nobody anything, while &lt;code&gt;/url-slug-length&lt;/code&gt; tells everybody what the page is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to leave out
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Dates.&lt;/strong&gt; A slug containing &lt;code&gt;/2024/&lt;/code&gt; or &lt;code&gt;october-update&lt;/code&gt; dates the page the moment it is published, and every later update makes the URL look stale. Dates belong in the page metadata, where they can be updated, not in the address, where they cannot be changed without breaking links.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meaningless IDs.&lt;/strong&gt; Numbers from a database, session tokens, and tracking parameters are not part of the page's identity. They make URLs unshareable — nobody pastes a link full of digits with confidence — and they create duplicate addresses for the same content, which is the canonical problem described in our &lt;a href="https://www.serpprism.com/guides/canonical-in-the-wild" rel="noopener noreferrer"&gt;canonical survey&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stopwords you do not need.&lt;/strong&gt; Words like "the", "and", "of", and "a" add length without adding meaning, and most slugs read better without them. But do not strip so aggressively that the slug stops making sense: &lt;code&gt;/how-long-url-slug-be&lt;/code&gt; is shorter than &lt;code&gt;/how-long-should-a-url-slug-be&lt;/code&gt; and harder to read. Remove the words that carry no information, keep the ones that hold the phrase together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repeated words and keyword lists.&lt;/strong&gt; A slug of &lt;code&gt;/seo-url-slug-seo-length-seo-guide&lt;/code&gt; is not more descriptive than &lt;code&gt;/url-slug-length&lt;/code&gt;, it is just longer. Repeating a word does not make the page more relevant to it; it makes the URL look generated, and generated-looking URLs get fewer clicks and fewer links.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing a slug: the 301 rule
&lt;/h2&gt;

&lt;p&gt;A slug is part of a page's address, and changing it breaks every link pointing at the old one. So the rule is simple: &lt;strong&gt;never change a slug without a 301 redirect from the old URL to the new one.&lt;/strong&gt; A 301 tells search engines and browsers that the page has permanently moved, and passes the old URL's accumulated links and history to the new one.&lt;/p&gt;

&lt;p&gt;That means a slug change is a small migration, not a typo fix. Before you change one, check what links to the old URL — internal links, external links, bookmarks, social posts, and any printed material. Update the internal links to point at the new URL directly, so they do not rely on the redirect, and leave the 301 in place indefinitely: removing it later breaks the external links you cannot update.&lt;/p&gt;

&lt;p&gt;The other consequence is that a bad slug on a page nobody links to yet is cheap to fix, and the same bad slug on a page with years of links is expensive. This is the real argument for getting slugs right at publication time. The cost of a good slug is thirty seconds of thought before publishing; the cost of fixing a bad one later is a redirect, a link audit, and months of waiting for search engines to consolidate the two addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Letting the title become the slug.&lt;/strong&gt; Most content systems generate the slug from the title automatically, and a long title produces a long slug. A title of "How Long Should a URL Slug Be, and Does It Even Matter for Rankings in 2026?" becomes a slug nobody can paste. Edit the generated slug down to the words that matter before publishing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using a different slug for the same page in different places.&lt;/strong&gt; A page linked as &lt;code&gt;/guide&lt;/code&gt; from the navigation and &lt;code&gt;/Guide&lt;/code&gt; from an email is two addresses for one page. Pick one canonical form — lowercase, hyphenated, no trailing variation — and use it everywhere, including in your sitemap and your canonical tag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Putting the whole category path in the slug.&lt;/strong&gt; Folders are fine for organisation, but a URL like &lt;code&gt;/blog/seo/technical/on-page/urls/how-long-should-a-url-slug-be&lt;/code&gt; buries the page under four levels of hierarchy. Two levels are usually enough; deeper than that, the folders are doing the labelling that the slug should be doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Changing slugs during a redesign.&lt;/strong&gt; A redesign is the moment slugs get "tidied up" in bulk, which is also the moment hundreds of redirects get created at once. Redesign the design; leave the addresses alone unless a slug is actively wrong, and if you must change them, change them one at a time with a redirect each.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;p&gt;For a new page, write the slug after the draft is finished, when you know what the page actually covers. Three to five words, lowercase, hyphens, topic words only — then read the full URL out loud. If you can say it to someone and they can type it from memory, it is right.&lt;/p&gt;

&lt;p&gt;For an existing page, audit the slugs that matter: the homepage's children, the pages with the most links, and the pages that appear in search results. Fix the ones that are unreadable, dated, or full of IDs — with a 301 each — and leave the rest alone. A slug that is merely inelegant is not worth a migration; a slug that misleads readers or breaks when pasted is.&lt;/p&gt;

&lt;p&gt;And add the slug to your pre-publish check. The &lt;a href="https://www.serpprism.com/guides/pre-publish-seo-checklist" rel="noopener noreferrer"&gt;pre-publish checklist&lt;/a&gt; covers the canonical, the title, and the description; the slug belongs on the same list, because it is set at the same moment and is just as hard to change afterwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;Slug length is not a ranking factor, and this guide does not claim otherwise. A long slug will not push a page down, and a short one will not lift it up. What a short slug does is make the page easier to read, share, and link to — and links and clicks are the things that do move rankings, one step removed.&lt;/p&gt;

&lt;p&gt;The three-to-five-word range is a working habit drawn from practice, not a measured threshold. Pages with longer slugs rank perfectly well, and pages with one-word slugs can be cryptic. Treat the range as a default to deviate from deliberately, not a rule to enforce blindly.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>html</category>
    </item>
    <item>
      <title>How Many Internal Links Should a Page Have?</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/how-many-internal-links-should-a-page-have-5cn6</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/how-many-internal-links-should-a-page-have-5cn6</guid>
      <description>&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;There is no correct number of internal links per page, and anyone selling you one — five, ten, a hundred — is selling a rule that was never measured. Link where a reader would genuinely want to go next, make sure every important page is reachable from the homepage within three clicks, and make sure no page is reachable from nowhere at all. Those three tests cover everything a number would pretend to cover.&lt;/p&gt;

&lt;p&gt;Internal links do three jobs at once, and the confusion starts because people count links as if only one job mattered. They help readers navigate, they tell crawlers which pages exist and how they relate, and they distribute the authority a site has earned across its pages. A count optimises none of the three. A reader who finds the next step, a crawler that can reach every page, and a new page that inherits links from established ones — that is what good internal linking looks like, and it has no fixed total.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why no number exists
&lt;/h2&gt;

&lt;p&gt;Google has never published a limit, and the old guideline of "no more than 100 links per page" was withdrawn years ago. It was a crawling concern from an era when pages were simpler and crawlers weaker, not a quality judgement, and it has not been relevant for a long time.&lt;/p&gt;

&lt;p&gt;The deeper reason is that pages differ. A short answer page may need two links: one to the detailed guide, one back to the section it belongs to. A long reference page may need thirty, because it touches thirty topics a reader might want to explore. A navigation page may need hundreds, because its entire job is to list destinations. Judging all three by one number would mean the short page is over-linked, the reference page is under-linked, and the navigation page is broken — and all three judgements would be wrong.&lt;/p&gt;

&lt;p&gt;What matters instead is whether each link earns its place. A link earns its place when a reader following it learns something the current page does not say, or reaches a tool that does something the current page only describes. A link that points at a page the reader has already read, or at a page with nothing new to add, is clutter — and clutter is the only real excess in internal linking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three-click rule
&lt;/h2&gt;

&lt;p&gt;If one number is worth keeping, it is not a count of links but a count of clicks: &lt;strong&gt;any important page should be reachable from the homepage within three clicks.&lt;/strong&gt; Homepage to section page is one, section page to article is two, article to related detail is three. Beyond three, a page is deep enough that both readers and crawlers reach it rarely, and rarely-visited pages are rarely indexed, rarely linked, and rarely read.&lt;/p&gt;

&lt;p&gt;Three is a working rule, not a law. A large site with thousands of pages will have some pages four clicks deep, and that is fine if those pages are genuinely minor. The rule is a diagnostic: if a page you care about is five clicks from the homepage, that is a structural problem, and the fix is not to add links to that page alone but to reconsider the section it lives in. Deep pages are usually a symptom of a section that grew without a plan.&lt;/p&gt;

&lt;p&gt;Note the direction of the fix. When a page is too deep, people add a link from the homepage to it, which technically satisfies the rule and helps nobody: a homepage with a hundred direct links is not navigation, it is a sitemap pasted into the header. The right fix is to give the page a proper parent — a section page that lists it alongside its siblings — so the path is short and the journey makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Orphan pages are the real problem
&lt;/h2&gt;

&lt;p&gt;An orphan page is one that nothing links to: no navigation entry, no link from another article, no mention anywhere on the site. The only way a crawler can find it is through the sitemap, and the only way a reader can find it is by knowing the URL. Our guide to &lt;a href="https://www.serpprism.com/guides/why-pages-arent-indexed" rel="noopener noreferrer"&gt;why pages are not indexed&lt;/a&gt; lists this as one of the most common causes of a technically perfect page never being indexed, and it is the internal-linking failure that actually costs traffic.&lt;/p&gt;

&lt;p&gt;Orphans happen quietly. A page is published, the author means to link to it from the overview article, the overview is never updated, and the page sits alone. A redesign drops a section page, and the ten articles under it lose their only parent. A draft is published from a preview link and nobody adds it to the index. None of these are dramatic, which is why they accumulate.&lt;/p&gt;

&lt;p&gt;Finding them is mechanical. List every URL in your sitemap, list every URL that receives at least one internal link, and compare the two sets. Any URL in the first list but not the second is an orphan. Fix it by linking to it from the page a reader would most naturally arrive from — usually the overview or the article that mentions its topic — and add that link to your publishing checklist so the next page does not repeat it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anchor text: say where the link goes
&lt;/h2&gt;

&lt;p&gt;The clickable words of a link should describe the destination. "Read our guide to meta description length" tells a reader what they will get; "click here" tells them nothing, and "read more" tells them slightly less than nothing. This matters for readers first — people scan links before clicking them — and for crawlers second, because anchor text is one of the signals used to understand what the destination page is about.&lt;/p&gt;

&lt;p&gt;Two habits to avoid. The first is using the same anchor text for different destinations, which tells both readers and crawlers that two different pages are the same thing. The second is forcing the exact same keyword phrase into every link to a page, which reads as manipulation and makes the text worse. Describe the destination in the words that fit the sentence you are writing, and vary them naturally.&lt;/p&gt;

&lt;p&gt;One more point worth stating: link from the body of the page, not only from navigation and footers. A link inside a paragraph, at the point where the reader is thinking about the topic, is worth more than the same link in a sidebar — to the reader, because it arrives when it is wanted, and to the crawler, because body links carry more weight than boilerplate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can a page have too many links?
&lt;/h2&gt;

&lt;p&gt;Yes, but the threshold is far higher than most people assume, and it is about usefulness rather than count. A page with two hundred links that a reader can scan — a directory, an index, a sitemap page — is fine. A page with twenty links where every second word is blue is not, because the text becomes unreadable and no single link stands out.&lt;/p&gt;

&lt;p&gt;The genuine excess case is sitewide boilerplate: the same fifty links in the footer of every page. Those links count as internal links, technically, but they teach a crawler nothing about the relationship between two specific pages, because they appear on all of them. If your link audit shows a page is "well linked" only because of the footer, it is not well linked at all. Count body links separately from template links, and judge the page by the body links.&lt;/p&gt;

&lt;p&gt;There is also a crawl-budget consideration on very large sites: every link is a URL a crawler may follow, and a site with millions of internal links can spend its crawl allowance re-walking the same navigation instead of reaching new content. For a site under a few thousand pages this is not a practical concern. For a large one, the fix is to stop linking to filtered, sorted, and paginated variants from every page, not to ration links on articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;p&gt;For a new page, link to at least two existing pages from the body, and link to the new page from at least one existing page — ideally the overview or article that a reader would arrive from. That is the whole rule for publishing, and it is also step seven of our &lt;a href="https://www.serpprism.com/guides/pre-publish-seo-checklist" rel="noopener noreferrer"&gt;pre-publish checklist&lt;/a&gt;. Two links out, one link in, every time, and orphans stop happening.&lt;/p&gt;

&lt;p&gt;For an existing site, run the orphan check described above, then check the click-depth of your ten most important pages. If any of them is more than three clicks from the homepage, restructure the section rather than bolting on a homepage link. Then read the five pages that earn the most traffic and ask of each link whether a reader would want it: remove the ones that point nowhere useful, and add the ones a reader would have wanted but did not get.&lt;/p&gt;

&lt;p&gt;Repeat the orphan check quarterly. Internal linking decays: pages get moved, sections get dropped, and links rot. A fifteen-minute check four times a year is the entire maintenance cost, and it catches the failure that no amount of content work can compensate for — a good page that nobody can find.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;The three-click rule is a heuristic from practice, not a measured threshold. Google has never stated that pages beyond three clicks are penalised, and deep pages do get indexed and do rank. The rule is useful because depth correlates with neglect — deep pages tend to be the ones nobody linked to, updated, or read — not because depth itself is a penalty.&lt;/p&gt;

&lt;p&gt;Likewise, the advice here assumes a site small enough that a human can hold its structure in mind. On a site with hundreds of thousands of pages, internal linking is a data problem solved with crawl data and templates, not a writing problem solved with checklists. The principles are the same; the tooling is not.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>marketing</category>
    </item>
    <item>
      <title>Image Alt Text: What to Write and How Long</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/image-alt-text-what-to-write-and-how-long-3519</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/image-alt-text-what-to-write-and-how-long-3519</guid>
      <description>&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;Alt text should say what the image does for a reader who cannot see it, in one sentence, in plain words. If the image is a chart, say what the chart shows. If it is a product photo, say what the product looks like. If it is purely decorative, leave the alt attribute empty. And keep keywords out of it: alt text is a description for people, not a place to store search terms.&lt;/p&gt;

&lt;p&gt;That is the whole rule. The reason it needs a guide at all is that alt text is written for three audiences at once — screen reader users, search engines, and readers whose images fail to load — and the habits that serve one audience are sometimes wrong for the others. The rule above serves all three, which is why it has survived every change in how search engines work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who actually reads alt text
&lt;/h2&gt;

&lt;p&gt;The first audience is people using screen readers, and they are the reason the attribute exists. A screen reader announces an image by reading its alt text aloud, in place of the image. A good description lets a blind reader follow the article; a missing or useless one — "image", "photo", a filename — leaves a gap in the middle of the sentence they were reading.&lt;/p&gt;

&lt;p&gt;The second audience is search engines. Crawlers cannot see images, so alt text is one of the signals they use to understand what an image shows, and it contributes to how the page is understood. It also feeds image search: a descriptive alt text is part of why an image surfaces for a query about its subject. But its influence on ordinary web ranking is small, and anyone telling you alt text is a major ranking lever is overstating it.&lt;/p&gt;

&lt;p&gt;The third audience is everyone, on a bad connection. When an image fails to load, the browser shows the alt text in its place. A reader on a slow network sees your description instead of a broken-image icon, and can decide whether the image was worth waiting for. This is the most common audience of all, and the one people forget, because they test pages on fast connections where images never fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to write it
&lt;/h2&gt;

&lt;p&gt;Describe the content and the function, not the file. Ask two questions: what would a reader miss if this image were removed, and what is the image doing in this spot? The answer to the first gives you the content; the answer to the second gives you the angle.&lt;/p&gt;

&lt;p&gt;A chart showing search traffic doubling after a redesign: "Line chart showing weekly search visits rising from 400 to 900 over eight weeks following the redesign." A product photo of a red kettle: "Red stainless-steel kettle with a black handle, photographed on a white background." A screenshot of a tool's output: "The SERP preview tool showing a title truncated at 60 characters with the cut marked in red." Each one says what the image contains and why it is there.&lt;/p&gt;

&lt;p&gt;The formula that works for most images is: what it is, plus the detail that matters, plus nothing else. "What it is" is the subject. "The detail that matters" is the one thing the surrounding text depends on — the number on the chart, the colour of the product, the error the screenshot shows. Anything beyond that is narration the reader did not ask for.&lt;/p&gt;

&lt;h2&gt;
  
  
  How long should it be?
&lt;/h2&gt;

&lt;p&gt;One sentence, and usually under 125 characters. That number is not a rule from a specification; it is a practical limit from screen readers, several of which split or cut off alt text beyond roughly that length. A description that runs to three sentences will be truncated mid-thought for the very audience it was written for.&lt;/p&gt;

&lt;p&gt;If one sentence cannot describe the image, the image is probably doing two jobs and should be split, or the description belongs in the visible text instead. A complex chart deserves a paragraph of explanation next to it, with the alt text carrying the summary: "Bar chart comparing load times before and after optimisation; full figures in the table below." The alt text points at the explanation; it does not try to be the explanation.&lt;/p&gt;

&lt;p&gt;Long alt text has one more cost: it bores the listener. A screen reader user navigating a page hears every image announced, and a fifty-word description for a stock photo of a handshake is fifty words of their time spent on nothing. Brevity here is not style, it is respect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decorative images get an empty alt
&lt;/h2&gt;

&lt;p&gt;Not every image needs a description. A divider, a background texture, a stock photo that adds mood but no information, an icon that sits next to a text label saying the same thing — these are decorative, and the correct alt text for a decorative image is an empty string: &lt;code&gt;alt=""&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Empty does not mean missing. An image with no alt attribute at all forces a screen reader to guess, and it usually guesses the filename — "hero-banner-final-v2.jpg" read aloud, in the middle of your article. An empty alt attribute tells the screen reader to skip the image entirely, which is exactly right for something that carries no information.&lt;/p&gt;

&lt;p&gt;The test is simple: if you removed the image and nothing in the article became harder to understand, it is decorative. Be honest about this. Most images on most pages are decorative, and the reason so many sites have bad alt text is that their authors wrote earnest descriptions for images that needed none.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Keyword stuffing.&lt;/strong&gt; Alt text reading "cheap running shoes, best running shoes, running shoes sale, buy running shoes" is not a description, it is a keyword list, and it is read aloud to screen reader users in exactly that form. It also does not help rankings: search engines have treated keyword-stuffed alt text as a spam signal for years. Describe the image once, in words a person would use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Starting with "image of" or "picture of".&lt;/strong&gt; A screen reader already announces that the element is an image, so "image of a dog" is heard as "image, image of a dog". Start with the subject: "A dog sleeping on a porch."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using the filename.&lt;/strong&gt;"IMG_4821.jpg" or "screenshot-2026-10-27-final.png" is not a description of anything. If the filename is the alt text, the alt text was never written.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repeating the caption.&lt;/strong&gt; If the image has a visible caption saying the same thing, the alt text should not repeat it word for word — a screen reader user hears it twice. Either shorten the alt text to the detail the caption omits, or, if the caption fully describes the image, leave the alt empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Describing text inside the image without transcribing it.&lt;/strong&gt; If an image contains words — a screenshot of an error message, a photo of a sign — the alt text should include those words, because they are the content. "Screenshot of an error" does not help; "Screenshot showing the error message 'Payment failed: card declined'" does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alt text and the rest of the page
&lt;/h2&gt;

&lt;p&gt;Alt text is one part of how a page presents its images, and it works alongside the other parts rather than replacing them. The file name should be readable too — &lt;code&gt;red-kettle.jpg&lt;/code&gt; rather than &lt;code&gt;IMG_4821.jpg&lt;/code&gt; — because file names are another signal about the image's subject, and because they are what people see when they save or share the file.&lt;/p&gt;

&lt;p&gt;Captions, titles, and surrounding text all contribute. A caption can carry the context — where the photo was taken, who is in it — while the alt text carries the description. The two should complement each other, not duplicate each other.&lt;/p&gt;

&lt;p&gt;And the same discipline applies to the tags that describe the page when it is shared. The &lt;a href="https://www.serpprism.com/tools/open-graph-preview" rel="noopener noreferrer"&gt;Open Graph preview tool&lt;/a&gt; on this site shows what a link's share card will look like, including whether its image renders at all — a related check, because a share card with a broken image and a page with undescribed images are the same failure in two places: the image was treated as decoration when it was doing real work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;p&gt;Audit the images on your ten most visited pages. For each one, ask whether it is decorative — if so, set an empty alt. If it is informative, write one sentence describing what it shows and why it matters, under 125 characters, without keywords and without "image of". If the image contains text, transcribe the words that matter.&lt;/p&gt;

&lt;p&gt;Then make it part of publishing, not a cleanup project. The &lt;a href="https://www.serpprism.com/guides/pre-publish-seo-checklist" rel="noopener noreferrer"&gt;pre-publish checklist&lt;/a&gt; already includes an alt text step; the rule to attach to it is one sentence long, and it fits on the same line: describe what the image does, or leave it empty. Images added later inherit the habit, and the audit never needs repeating at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;The 125-character guide is a practical limit from screen reader behaviour, not a specification, and different screen readers handle long alt text differently. Treat it as a default, and let a genuinely complex image exceed it when the alternative is leaving out information a reader needs.&lt;/p&gt;

&lt;p&gt;Alt text also does not make an inaccessible image accessible on its own. A chart whose data matters needs a data table or a text summary alongside it; a diagram that explains a process needs that process explained in words. Alt text is the label on the image, not a substitute for the content the image carries.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>html</category>
    </item>
    <item>
      <title>nofollow vs sponsored vs ugc: Which to Use</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/nofollow-vs-sponsored-vs-ugc-which-to-use-57ke</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/nofollow-vs-sponsored-vs-ugc-which-to-use-57ke</guid>
      <description>&lt;h2&gt;
  
  
  The short answer
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;rel="sponsored"&lt;/code&gt; for links you were paid for, &lt;code&gt;rel="ugc"&lt;/code&gt; for links your users created, and &lt;code&gt;rel="nofollow"&lt;/code&gt; for links you do not trust or cannot vouch for. That is the entire decision in one sentence. Everything else in this guide is about the cases where the answer is not obvious, and about what these attributes actually do now that Google treats all three as hints rather than commands.&lt;/p&gt;

&lt;p&gt;The three values exist because one value was doing three jobs badly. For years, &lt;code&gt;nofollow&lt;/code&gt; was the only option, so it got used for paid links, for comment links, and for untrusted links alike, and nobody reading the markup could tell which was which. Splitting them apart was a labelling fix: the same link, described accurately.&lt;/p&gt;

&lt;h2&gt;
  
  
  What each one means
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;sponsored&lt;/code&gt; means money changed hands.&lt;/strong&gt; Any link that is part of an advertisement, a sponsorship, or a paid placement — including a product a company sent you for free in exchange for a link — should carry &lt;code&gt;rel="sponsored"&lt;/code&gt;. This is the one with the clearest rule behind it, because paid links that pass ranking credit are a manipulation of search results, and search engines and regulators both treat undisclosed paid links as a violation. If a link exists because someone paid for it, mark it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ugc&lt;/code&gt; means a user wrote it.&lt;/strong&gt; Links in comments, forum posts, reviews, and any other content your visitors contribute should carry &lt;code&gt;rel="ugc"&lt;/code&gt;. The point is to separate the links you chose from the links your users chose. You did not vet them, you did not endorse them, and you should not have to: the attribute says the link came from a user, and the search engine can weigh it accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;nofollow&lt;/code&gt; means you do not vouch for it.&lt;/strong&gt; Use it for links to pages you do not trust, or where you have no basis to endorse the destination — a link in a guest post you did not fully review, a link to a site you have not checked, a link you are including for completeness rather than recommendation. It is the general-purpose "no endorsement" marker, and it is also the fallback when none of the other two fits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision, as a table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The link is…&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Paid, sponsored, or exchanged for a product&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sponsored&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Money must be disclosed; unmarked paid links are a violation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Written by a user: comment, forum post, review&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ugc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Separates your editorial links from your users' links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One you do not trust or have not vetted&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nofollow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Withholds endorsement without deleting the link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A normal editorial link you chose&lt;/td&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;An ordinary link is an endorsement; marking it would be wrong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Paid and user-written at once&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sponsored ugc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The values combine; use both&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the last row carefully, because it answers the question people ask most: yes, the values can be combined. &lt;code&gt;rel="sponsored ugc"&lt;/code&gt; is valid, and so is &lt;code&gt;rel="nofollow sponsored"&lt;/code&gt;. They are labels, not radio buttons, and a link can truthfully carry more than one.&lt;/p&gt;

&lt;h2&gt;
  
  
  They are hints now, not commands
&lt;/h2&gt;

&lt;p&gt;Since 2019, Google treats all three values as hints rather than directives. Before that, &lt;code&gt;nofollow&lt;/code&gt; was an instruction: do not follow this link, do not pass ranking credit through it. Now it is a suggestion that Google may follow or ignore, depending on its own judgement of the link and the page.&lt;/p&gt;

&lt;p&gt;In practice this changes less than it sounds. Google still generally honours the hint, and a page full of marked links does not pass credit the way an unmarked page does. What changed is that Google reserves the right to use a marked link as a discovery signal — to find a new page through it, for example — even when it does not pass ranking credit. So marking a link no longer guarantees it is invisible to crawlers. If a link must not be followed at all, the mechanism is robots.txt, not a rel attribute.&lt;/p&gt;

&lt;p&gt;The other practical consequence is that mislabelling is now less catastrophic than it was. A paid link marked &lt;code&gt;ugc&lt;/code&gt; instead of &lt;code&gt;sponsored&lt;/code&gt; is still wrong — the disclosure is inaccurate — but it is not the total loss of control it would have been under the old directive model. Fix it anyway. The attribute is a statement about the link, and statements should be accurate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Marking every outbound link nofollow.&lt;/strong&gt; Some sites do this as a blanket policy, on the theory that it "preserves" ranking credit. It does not preserve anything — credit is not a substance that leaks out of links — and it makes the site's markup dishonest: every link claims no endorsement, including the ones the author genuinely recommends. Mark links by what they are, not by policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Marking internal links.&lt;/strong&gt; The three values describe how a link should be treated by search engines, and they are meant for links to other sites. Putting &lt;code&gt;nofollow&lt;/code&gt; on your own internal links does not concentrate authority on the remaining pages; it just makes your own site harder to crawl and understand. Internal links should almost always be plain links.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using nofollow for paid links.&lt;/strong&gt; It was the old habit, and it is now the wrong label. A paid link is sponsored, and calling it nofollow hides the reason it exists. Google has said explicitly that &lt;code&gt;sponsored&lt;/code&gt; is the correct value for paid links, and using the wrong one is a disclosure failure, not a harmless synonym.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting that comments need it.&lt;/strong&gt; A comment section with unmarked links is an open invitation to spam, because every posted link passes endorsement by default. If your site accepts user content, the &lt;code&gt;ugc&lt;/code&gt; attribute should be applied by the template, not by hand — one line in the rendering code, not a decision per comment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treating the attribute as a security measure.&lt;/strong&gt; Marking a link does not make its destination safe, does not stop users clicking it, and does not prevent crawlers from ever visiting it. It is a statement about endorsement and ranking credit, nothing more. A link to a malicious site should be removed, not marked.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;p&gt;Audit your templates first, because most links on a site are generated, not written by hand. Check that comment and forum templates add &lt;code&gt;ugc&lt;/code&gt; automatically, that any advertising or sponsored-content template adds &lt;code&gt;sponsored&lt;/code&gt;, and that editorial content templates add nothing. One template fix covers every future link; one manual fix covers one link.&lt;/p&gt;

&lt;p&gt;Then check the links you wrote by hand: affiliate links, sponsored mentions, and product links in reviews. Affiliate links are paid links — the commission is the payment — so they take &lt;code&gt;sponsored&lt;/code&gt;, and the same applies to a product sent to you for review. If you are unsure whether a link counts as paid, ask whether the link would exist without the payment. If the answer is no, mark it.&lt;/p&gt;

&lt;p&gt;Finally, do not go back and re-mark old links in bulk unless they are paid links, because mislabelled paid links are the one case with real consequences. Ordinary editorial links that carry a stray &lt;code&gt;nofollow&lt;/code&gt; from an old blanket policy are worth cleaning up when you touch the page anyway, but they are not worth a dedicated migration. The &lt;a href="https://www.serpprism.com/guides/pre-publish-seo-checklist" rel="noopener noreferrer"&gt;pre-publish checklist&lt;/a&gt; includes a link review step; add the rel question to it, and the problem stops recurring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;Google's treatment of these values as hints means the effect of marking a link is not directly observable from outside. We cannot measure, for a given link, whether credit passed or not, because that is an internal judgement. What we can verify is the labelling: whether the markup accurately describes the link. That is the part this guide is about, and it is the part you control.&lt;/p&gt;

&lt;p&gt;Other search engines and AI crawlers may treat these values differently, and none has published a complete account of how. The values are a web standard for describing links, so using them accurately is correct regardless of which system reads them — but do not assume every system reads them the same way Google does.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>html</category>
      <category>marketing</category>
    </item>
    <item>
      <title>I built a tax estimator for freelancers because QuickBooks is overkill</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Wed, 21 Oct 2026 10:50:55 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/i-built-a-tax-estimator-for-freelancers-because-quickbooks-is-overkill-2nam</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/i-built-a-tax-estimator-for-freelancers-because-quickbooks-is-overkill-2nam</guid>
      <description>&lt;p&gt;Freelancers: you don't need QuickBooks to stay tax-ready.&lt;/p&gt;

&lt;p&gt;You need to know how much to set aside each quarter.&lt;/p&gt;

&lt;p&gt;Built a tool that does exactly that — upload a CSV, get your quarterly tax number in ~5 minutes. $9/mo, 7-day trial.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sololedger-1us.pages.dev" rel="noopener noreferrer"&gt;https://sololedger-1us.pages.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>freelance</category>
      <category>tax</category>
      <category>sass</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Nine SEO checks to run before you publish a page</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Wed, 23 Sep 2026 03:35:11 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/nine-seo-checks-to-run-before-you-publish-a-page-3de0</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/nine-seo-checks-to-run-before-you-publish-a-page-3de0</guid>
      <description>&lt;h2&gt;
  
  
  The problem with SEO checklists
&lt;/h2&gt;

&lt;p&gt;Most of them list things you can see. The real problems are the ones you can't.&lt;/p&gt;

&lt;p&gt;A missing canonical, a duplicated &lt;code&gt;h1&lt;/code&gt;, a meta description that gets truncated — none of these break anything. The page renders, the content is good, and you find out weeks later in a report nobody reads closely.&lt;/p&gt;

&lt;p&gt;So this is a list of checks for &lt;em&gt;invisible&lt;/em&gt; failures. The order is deliberate: structural checks first, because fixing structure usually changes the text, and re-running the content checks after a rewrite wastes the first pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Does the page have exactly one &lt;code&gt;h1&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Not zero, not two.&lt;/p&gt;

&lt;p&gt;Zero usually means the title was styled with a class instead of a heading element. Two usually means a template printed the site name as an &lt;code&gt;h1&lt;/code&gt; and the page title as another one.&lt;/p&gt;

&lt;p&gt;Both are invisible in a browser, and both make the page's main subject ambiguous.&lt;/p&gt;

&lt;p&gt;Read the heading outline rather than an issue count. If the &lt;code&gt;h2&lt;/code&gt; list reads like a table of contents for the page, the structure is fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Do heading levels descend without skipping?
&lt;/h2&gt;

&lt;p&gt;An &lt;code&gt;h2&lt;/code&gt; followed by an &lt;code&gt;h4&lt;/code&gt; tells a reader a level is missing.&lt;/p&gt;

&lt;p&gt;Sometimes a level genuinely is missing and the outline is still correct — but more often the &lt;code&gt;h4&lt;/code&gt; was chosen for its smaller font size.&lt;/p&gt;

&lt;p&gt;Choose levels for meaning and style them in CSS. Nothing in HTML requires an &lt;code&gt;h2&lt;/code&gt; to be visually larger than an &lt;code&gt;h3&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Is the title tag specific to this page?
&lt;/h2&gt;

&lt;p&gt;Open ten tabs of your site and read the titles.&lt;/p&gt;

&lt;p&gt;If several are identical, those pages compete for the same query and none of them wins clearly. A title should describe what &lt;em&gt;this&lt;/em&gt; page covers, not the category it belongs to.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Will the description survive truncation?
&lt;/h2&gt;

&lt;p&gt;The widely quoted 155-character limit is an average, not a rule. The real constraint is rendered pixel width, and mobile is meaningfully narrower than desktop.&lt;/p&gt;

&lt;p&gt;Put the sentence that must survive in the first ~120 characters and treat the rest as elaboration.&lt;/p&gt;

&lt;p&gt;Preview it rather than counting — measure in pixels, not characters.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Does the canonical point at itself?
&lt;/h2&gt;

&lt;p&gt;A page's canonical should be its own URL, &lt;strong&gt;in the same form the page is served at&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Trailing slashes are the common case. If &lt;code&gt;/tools&lt;/code&gt; redirects to &lt;code&gt;/tools/&lt;/code&gt;, the canonical must say &lt;code&gt;/tools/&lt;/code&gt; — otherwise you're telling Google the real page is a URL that redirects.&lt;/p&gt;

&lt;p&gt;Check index pages specifically. They're the ones most often generated with a canonical that omits the trailing slash.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Is the page in the sitemap, in the right URL form?
&lt;/h2&gt;

&lt;p&gt;Same trailing-slash question. A sitemap entry that redirects is a wasted crawl.&lt;/p&gt;

&lt;p&gt;If your sitemap is generated, generate it from the same value as the canonical. Two independent string constructions will eventually disagree — and nothing will tell you when they do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Wrong: two places that must agree, with nothing enforcing it&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canonical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SITE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SITE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// ← drifts&lt;/span&gt;

&lt;span class="c1"&gt;// Right: one value, used twice&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SITE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/blog/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Are the internal links real and useful?
&lt;/h2&gt;

&lt;p&gt;Every new page should link to at least two existing pages, and at least one existing page should link to it.&lt;/p&gt;

&lt;p&gt;A page nothing links to is a page that's hard to find. Internal links are the only navigation signal you fully control.&lt;/p&gt;

&lt;p&gt;Use descriptive anchor text. "Read the guide" tells a reader nothing about where it goes.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Do images have dimensions and alt text?
&lt;/h2&gt;

&lt;p&gt;Set explicit &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; on every image.&lt;/p&gt;

&lt;p&gt;Without them the browser can't reserve space, and the page shifts as images load — the single most common cause of a bad layout-shift score.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/chart.png"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"800"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"420"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Response time by region"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alt text describes the image for someone who can't see it. If the image is purely decorative, &lt;code&gt;alt=""&lt;/code&gt; is correct — and better than a description nobody needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Does the structured data describe this page accurately?
&lt;/h2&gt;

&lt;p&gt;If you mark up an &lt;code&gt;Article&lt;/code&gt;, the headline should match the visible title and the dates should be real.&lt;/p&gt;

&lt;p&gt;If you mark up an &lt;code&gt;FAQPage&lt;/code&gt;, those questions must be visible on the page. Markup describing content that isn't there is a violation, not an optimisation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is deliberately not on this list
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Keyword density.&lt;/strong&gt; There's no target to hit, and writing to one produces worse text. Run a density check only to see whether the page is actually about the topic you intended — if the top phrases surprise you, that's a content problem, not a density problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Word count.&lt;/strong&gt; Length is a consequence of covering the subject, not a goal. A page that answers the question in 400 words is finished. Padding it to 1200 makes it worse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meta keywords.&lt;/strong&gt; Ignored for over a decade.&lt;/p&gt;




&lt;p&gt;The full version of this, with the reasoning behind each item, is on &lt;a href="https://www.serpprism.com/guides/pre-publish-seo-checklist" rel="noopener noreferrer"&gt;SerpPrism&lt;/a&gt;. The tools mentioned above — &lt;a href="https://www.serpprism.com/tools/heading-analyzer" rel="noopener noreferrer"&gt;heading analyzer&lt;/a&gt;, &lt;a href="https://www.serpprism.com/tools/serp-preview" rel="noopener noreferrer"&gt;SERP preview&lt;/a&gt;, &lt;a href="https://www.serpprism.com/tools/robots-txt-tester" rel="noopener noreferrer"&gt;robots.txt tester&lt;/a&gt; — all run client-side, nothing is uploaded.&lt;/p&gt;

&lt;p&gt;Curious whether anyone has a check I've missed. The one that keeps biting me is #5, because the failure only shows up on index pages.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>html</category>
      <category>a11y</category>
    </item>
    <item>
      <title>I checked the Open Graph tags on 62 homepages. Here's what's actually broken.</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Wed, 23 Sep 2026 03:35:02 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/i-checked-the-open-graph-tags-on-62-homepages-heres-whats-actually-broken-53g1</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/i-checked-the-open-graph-tags-on-62-homepages-heres-whats-actually-broken-53g1</guid>
      <description>&lt;p&gt;Open Graph tags don't affect rankings. They decide something narrower and easier to forget: whether your link renders as a card with an image, or as a bare grey link that people scroll past.&lt;/p&gt;

&lt;p&gt;I got curious about what's actually in the wild, so I fetched the homepages of 78 well-known domains and parsed every &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag whose &lt;code&gt;property&lt;/code&gt; or &lt;code&gt;name&lt;/code&gt; started with &lt;code&gt;og:&lt;/code&gt; or &lt;code&gt;twitter:&lt;/code&gt;. 62 came back usable.&lt;/p&gt;

&lt;p&gt;Here's what I found, including one failure mode I had never considered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method, and the two counting rules that changed the numbers
&lt;/h2&gt;

&lt;p&gt;78 domains, 62 usable homepages. Two rules mattered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An empty or missing &lt;code&gt;content&lt;/code&gt; attribute counts as absent.&lt;/strong&gt; &lt;code&gt;og:title=""&lt;/code&gt; is a tag that exists and says nothing. If you count it as present, you inflate your numbers and — as it turns out — hide the most interesting bug in the dataset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No JavaScript was executed.&lt;/strong&gt; This is server-rendered HTML only. That's usually fine for social tags (they're server-rendered &lt;em&gt;because&lt;/em&gt; crawlers don't run JS), but it means every percentage below is a floor, not a rate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 1: Open Graph is near-universal. Twitter cards are not.
&lt;/h2&gt;

&lt;p&gt;56 of 62 homepages (90%) had at least one Open Graph tag. But that headline hides how patchy individual fields are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;tag&lt;/th&gt;
&lt;th&gt;count&lt;/th&gt;
&lt;th&gt;%&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;og:title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;54/62&lt;/td&gt;
&lt;td&gt;87%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;og:description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;53/62&lt;/td&gt;
&lt;td&gt;85%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;og:url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;49/62&lt;/td&gt;
&lt;td&gt;79%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;og:image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;48/62&lt;/td&gt;
&lt;td&gt;77%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;og:type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;48/62&lt;/td&gt;
&lt;td&gt;77%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;og:site_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;40/62&lt;/td&gt;
&lt;td&gt;65%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;og:locale&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;22/62&lt;/td&gt;
&lt;td&gt;35%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So about one homepage in four has no shareable image.&lt;/p&gt;

&lt;p&gt;The Twitter side is where the real gap is. Of the 62:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;34 (55%) declared &lt;code&gt;twitter:card&lt;/code&gt; = &lt;code&gt;summary_large_image&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;14 (23%) = &lt;code&gt;summary&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;2 (3%) = &lt;code&gt;app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;12 (19%) declared no &lt;code&gt;twitter:card&lt;/code&gt; at all&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last group is the one to care about. X falls back to Open Graph for title, description and image — but it still needs &lt;code&gt;twitter:card&lt;/code&gt; to know &lt;em&gt;which layout to build&lt;/em&gt;. Without it you get a plain link, no matter how good your Open Graph tags are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2: two sites ship an &lt;code&gt;og:image&lt;/code&gt; tag with nothing in it
&lt;/h2&gt;

&lt;p&gt;I expected this to be common. It wasn't: only 2 of the 34 sites declaring &lt;code&gt;summary_large_image&lt;/code&gt; had no usable image. But the two fail differently, and one of them is genuinely sneaky.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;netflix.com&lt;/strong&gt; declares &lt;code&gt;twitter:card = summary_large_image&lt;/code&gt; and &lt;code&gt;twitter:site&lt;/code&gt;, and nothing else. No &lt;code&gt;og:image&lt;/code&gt;, no &lt;code&gt;og:title&lt;/code&gt;, no &lt;code&gt;og:url&lt;/code&gt;. The card type promises a large image that was never supplied.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;forbes.com&lt;/strong&gt; does emit the tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt; &lt;span class="na"&gt;data-next-head=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no &lt;code&gt;content&lt;/code&gt; attribute at all.&lt;/p&gt;

&lt;p&gt;This is the failure mode worth internalising. A &lt;em&gt;missing&lt;/em&gt; tag fails loudly — any validator catches it. A &lt;em&gt;present-but-empty&lt;/em&gt; tag passes every "is it there?" check and renders nothing. If your audit tool asks "does &lt;code&gt;og:image&lt;/code&gt; exist?", Forbes returns yes.&lt;/p&gt;

&lt;p&gt;Its &lt;code&gt;twitter:image&lt;/code&gt; has the same shape, and its &lt;code&gt;og:image:type&lt;/code&gt; is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image:type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"image/jpeg,image/gif,image/png"&lt;/span&gt; &lt;span class="na"&gt;data-next-head=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A comma-separated list. Not a single MIME type. Not a valid value for that field.&lt;/p&gt;

&lt;p&gt;You can spot this in one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://example.com | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;meta[^&amp;gt;]*og:image[^&amp;gt;]*&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the tag shows up with no &lt;code&gt;content=&lt;/code&gt;, you've found it.&lt;/p&gt;

&lt;p&gt;I should be candid that I had this exactly backwards: until the day I collected this data, all 25 pages on my own site declared &lt;code&gt;summary_large_image&lt;/code&gt; with no &lt;code&gt;og:image&lt;/code&gt;. The pattern that turns out to be rare in the wild was on every page of my site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3: only 23% declare image dimensions
&lt;/h2&gt;

&lt;p&gt;Of the 48 homepages with a usable &lt;code&gt;og:image&lt;/code&gt;, just &lt;strong&gt;11 (23%)&lt;/strong&gt; also declared &lt;code&gt;og:image:width&lt;/code&gt; and &lt;code&gt;og:image:height&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This sounds harmless. It isn't, and the reason is subtle: without dimensions the platform has to download the image before it can lay out the card. If that fetch is slow, rate-limited, or blocked — a CDN that dislikes the crawler's user agent, a region where the image host is slow — &lt;strong&gt;the first share goes out with no image and the second one works.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An intermittent bug like that is far harder to diagnose than a missing tag, because by the time you look, it works.&lt;/p&gt;

&lt;p&gt;The 11 that do declare dimensions: &lt;code&gt;figma.com&lt;/code&gt;, &lt;code&gt;x.com&lt;/code&gt;, &lt;code&gt;docker.com&lt;/code&gt;, &lt;code&gt;developer.mozilla.org&lt;/code&gt;, &lt;code&gt;w3.org&lt;/code&gt;, &lt;code&gt;moz.com&lt;/code&gt;, &lt;code&gt;screamingfrog.co.uk&lt;/code&gt;, &lt;code&gt;yoast.com&lt;/code&gt;, &lt;code&gt;searchenginejournal.com&lt;/code&gt;, &lt;code&gt;harvard.edu&lt;/code&gt;, &lt;code&gt;heroku.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Four of those — Moz, Screaming Frog, Yoast, Search Engine Journal — are SEO vendors or publications. &lt;strong&gt;All four&lt;/strong&gt; SEO sites in the sample declare dimensions, against 23% overall. They're also the same four that showed up as &lt;code&gt;@graph&lt;/code&gt; users when I ran the equivalent survey on JSON-LD. Consistent signal: the sites whose business is this metadata treat it better than average by a wide margin.&lt;/p&gt;

&lt;p&gt;Only 5 of 48 (10%) declared &lt;code&gt;og:image:alt&lt;/code&gt;. Alt text on a social card isn't decorative — it's what a screen reader announces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 4: one in four &lt;code&gt;og:title&lt;/code&gt; disagrees with &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;14 of the 54 homepages with both showed different text in each (26%).&lt;/p&gt;

&lt;p&gt;Most are trivial: &lt;code&gt;github.com&lt;/code&gt; drops a trailing "· GitHub" from the Open Graph version. Some aren't. &lt;code&gt;kubernetes.io&lt;/code&gt; ships:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Kubernetes&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Production-Grade Container Orchestration"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The search result says one thing; the shared link says another. Neither is &lt;em&gt;wrong&lt;/em&gt; — but it means the title you optimised for search isn't the one people see when the page is shared, and those are written for different purposes. If you only check one, check the one that appears in search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 5: the things I expected and didn't find
&lt;/h2&gt;

&lt;p&gt;Across 48 usable images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; relative path (&lt;code&gt;kubernetes.io&lt;/code&gt;, &lt;code&gt;/images/kubernetes-open-graph.png&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0&lt;/strong&gt; &lt;code&gt;http://&lt;/code&gt; images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0&lt;/strong&gt; sites declaring more than one &lt;code&gt;og:image&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Relative &lt;code&gt;og:image&lt;/code&gt; URLs are the classic warning — scrapers have no base URL to resolve against, so the tag gets dropped. It happened once in 48. Insecure images, not at all.&lt;/p&gt;

&lt;p&gt;This is the third survey in a row where a widely repeated warning turned out to be rare at this sample size. That's not an argument for ignoring the warnings. It's an argument for checking the base rate before you spend a day on the thing you're worried about.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check on your own tags
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is the image tag carrying anything?&lt;/strong&gt; View source, look for &lt;code&gt;content=&lt;/code&gt;. Present-but-empty is the check no validator does for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you declare width and height?&lt;/strong&gt; Two lines, kills the "first share has no image" failure mode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you declare &lt;code&gt;twitter:card&lt;/code&gt;?&lt;/strong&gt; One in five sites skip it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does &lt;code&gt;og:title&lt;/code&gt; match your title?&lt;/strong&gt; If not, pick which one you meant.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full dataset — including the raw HTML for all 62 domains, so you can check any claim against the markup rather than my summary of it — is on &lt;a href="https://www.serpprism.com/guides/open-graph-in-the-wild" rel="noopener noreferrer"&gt;SerpPrism&lt;/a&gt;. There's also a &lt;a href="https://www.serpprism.com/tools/open-graph-preview" rel="noopener noreferrer"&gt;preview tool&lt;/a&gt; that renders what your tags will actually produce.&lt;/p&gt;

&lt;p&gt;If you find a row that's wrong, the markup settles it — and I'd genuinely like to know.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>html</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Your JSON-LD is duplicated in three places. Here's the fix.</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Wed, 23 Sep 2026 03:34:12 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/your-json-ld-is-duplicated-in-three-places-heres-the-fix-3pf8</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/your-json-ld-is-duplicated-in-three-places-heres-the-fix-3pf8</guid>
      <description>&lt;h2&gt;
  
  
  The pattern almost everyone starts with
&lt;/h2&gt;

&lt;p&gt;You add an &lt;code&gt;Organization&lt;/code&gt; block to the footer. An &lt;code&gt;Article&lt;/code&gt; block to the article template. A &lt;code&gt;BreadcrumbList&lt;/code&gt; to the breadcrumb component. Three separate &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; blocks, each describing one thing.&lt;/p&gt;

&lt;p&gt;It works — Google reads all of them — and it's where most sites stop.&lt;/p&gt;

&lt;p&gt;The cost isn't visible immediately. It shows up the first time you rename the company, change the logo URL, or move domains. Now you have three places containing the same facts, and nothing enforces that they agree. One gets updated, the others don't, and your structured data now contradicts itself in a way &lt;strong&gt;no error message will tell you about.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;@id&lt;/code&gt; actually does
&lt;/h2&gt;

&lt;p&gt;Every node in JSON-LD can carry an &lt;code&gt;@id&lt;/code&gt; — a stable identifier. A URL is conventional.&lt;/p&gt;

&lt;p&gt;Once a node has an &lt;code&gt;@id&lt;/code&gt;, any other node can &lt;em&gt;refer&lt;/em&gt; to it instead of repeating its properties.&lt;/p&gt;

&lt;p&gt;So instead of the Article containing its own copy of the publisher's name and logo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publisher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Organization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"logo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ImageObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/logo.png"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…it contains a reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"publisher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/#organization"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full description lives once, in a node other nodes point at. Change the logo in that one place and every reference is correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape that works
&lt;/h2&gt;

&lt;p&gt;One &lt;code&gt;@graph&lt;/code&gt; array in the page head, containing in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Organization&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WebSite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WebPage&lt;/code&gt; (for the current URL)&lt;/li&gt;
&lt;li&gt;whatever page-specific nodes apply — &lt;code&gt;Article&lt;/code&gt;, &lt;code&gt;FAQPage&lt;/code&gt;, &lt;code&gt;HowTo&lt;/code&gt;, &lt;code&gt;Product&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Organization and WebSite nodes are identical on every page — that's the point. They're declared once per page but defined consistently, and page-specific nodes reference them by &lt;code&gt;@id&lt;/code&gt; rather than restating them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://schema.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@graph"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Organization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/#organization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WebSite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/#website"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"publisher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/#organization"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WebPage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/blog/post#webpage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/blog/post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"isPartOf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/#website"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"datePublished"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-14"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dateModified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-02"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/blog/post#article"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"mainEntityOfPage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/blog/post#webpage"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"publisher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"@id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://acme.com/#organization"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four nodes, one block, every relationship explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways this goes wrong
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Referencing an &lt;code&gt;@id&lt;/code&gt; that doesn't exist on the page.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the Article says its publisher is &lt;code&gt;#organization&lt;/code&gt; but no node with that &lt;code&gt;@id&lt;/code&gt; is present, the reference resolves to nothing. Google handles this by &lt;em&gt;ignoring the property&lt;/em&gt; — so the symptom isn't an error, it's &lt;strong&gt;silently missing data&lt;/strong&gt;. Always include the node you reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Reusing the same &lt;code&gt;@id&lt;/code&gt; for different things.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Identifiers must be unique. Two nodes sharing an &lt;code&gt;@id&lt;/code&gt; means you've declared two contradictory definitions of one entity, and the outcome depends on which one a consumer reads first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Relative &lt;code&gt;@id&lt;/code&gt; values.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use absolute URLs. A relative reference like &lt;code&gt;#organization&lt;/code&gt; is interpreted against the page URL, which means the same markup on two domains produces two different identifiers — and any cross-page relationship you were expressing breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dates deserve their own section
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;datePublished&lt;/code&gt; should never change after the page goes live.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dateModified&lt;/code&gt; should change when the content genuinely changes, and not otherwise.&lt;/p&gt;

&lt;p&gt;Incrementing &lt;code&gt;dateModified&lt;/code&gt; on every deploy is a common habit, usually adopted because someone believed freshness helps rankings. It doesn't — and the pattern is trivially detectable: a modified date that advances daily while the words stay identical tells any consumer the field is noise, which means it gets discounted on the pages where it was actually true.&lt;/p&gt;

&lt;p&gt;If you use a build system, wire &lt;code&gt;dateModified&lt;/code&gt; to a &lt;strong&gt;per-page content hash&lt;/strong&gt;, not the build timestamp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// store hash → date in a committed JSON file&lt;/span&gt;
&lt;span class="c1"&gt;// update the date only when the hash changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The field then moves only when the content does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking your markup
&lt;/h2&gt;

&lt;p&gt;Validate with Google's Rich Results Test (for eligibility) and the Schema Markup Validator (for syntax).&lt;/p&gt;

&lt;p&gt;Neither will tell you that your &lt;code&gt;@id&lt;/code&gt; references are dangling — &lt;strong&gt;a dangling reference is not a syntax error.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The check that catches it takes a minute and is manual:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract every &lt;code&gt;@id&lt;/code&gt; &lt;strong&gt;defined&lt;/strong&gt; in the page&lt;/li&gt;
&lt;li&gt;Extract every value used in a &lt;strong&gt;reference&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Confirm every reference appears in the defined list&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On a page with six nodes this is faster by eye than to automate.&lt;/p&gt;




&lt;p&gt;I built a small generator for this shape that validates required properties as you type — &lt;a href="https://www.serpprism.com/tools/schema-markup-generator" rel="noopener noreferrer"&gt;SerpPrism's JSON-LD generator&lt;/a&gt;. It's client-side, nothing is uploaded. There's also a longer version of this post with a few more edge cases on the &lt;a href="https://www.serpprism.com/guides/json-ld-graph-structure" rel="noopener noreferrer"&gt;site itself&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy to hear how other people handle the dateModified problem — it's the one I've seen go wrong most often in production.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>What 61 Homepages Actually Ship in JSON-LD</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Tue, 22 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/what-61-homepages-actually-ship-in-json-ld-1jfc</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/what-61-homepages-actually-ship-in-json-ld-1jfc</guid>
      <description>&lt;h2&gt;
  
  
  How this was measured
&lt;/h2&gt;

&lt;p&gt;On 2026-09-23 we fetched the homepage of 78 well-known domains and extracted every &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; block from the response. 61 returned a usable page — 42 over a direct connection and 19 over a local proxy. Of those 61, 32 contained at least one JSON-LD block.&lt;/p&gt;

&lt;p&gt;One caveat governs everything below, so it belongs at the top rather than the bottom: &lt;strong&gt;this measures what is present in the server-rendered HTML.&lt;/strong&gt; A page that injects its structured data with JavaScript will look empty here even though a browser — and Google, which renders — would see it. Single-page applications are the obvious case. So when this article says a site "ships none", read it as "none in the initial HTML response", which is a different and weaker claim.&lt;/p&gt;

&lt;p&gt;For the sites where we did find markup, that caveat disappears: everything about blocks, &lt;code&gt;@graph&lt;/code&gt;, &lt;code&gt;@id&lt;/code&gt; and node types is measured on markup that was actually present.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 1: about half ship nothing in the HTML
&lt;/h2&gt;

&lt;p&gt;29 of the 61 homepages contained no JSON-LD block at all. The list includes &lt;code&gt;github.com&lt;/code&gt;, &lt;code&gt;youtube.com&lt;/code&gt;, &lt;code&gt;wikipedia.org&lt;/code&gt;, &lt;code&gt;w3.org&lt;/code&gt;, &lt;code&gt;mozilla.org&lt;/code&gt;, &lt;code&gt;linkedin.com&lt;/code&gt;, &lt;code&gt;x.com&lt;/code&gt;, &lt;code&gt;spotify.com&lt;/code&gt;, &lt;code&gt;npmjs.com&lt;/code&gt;, &lt;code&gt;nodejs.org&lt;/code&gt;, &lt;code&gt;kubernetes.io&lt;/code&gt;, &lt;code&gt;mit.edu&lt;/code&gt;, &lt;code&gt;stanford.edu&lt;/code&gt;, &lt;code&gt;khanacademy.org&lt;/code&gt;, &lt;code&gt;theguardian.com&lt;/code&gt;, &lt;code&gt;slack.com&lt;/code&gt;, &lt;code&gt;notion.so&lt;/code&gt;, &lt;code&gt;trello.com&lt;/code&gt;, &lt;code&gt;tumblr.com&lt;/code&gt;, &lt;code&gt;vimeo.com&lt;/code&gt;, &lt;code&gt;archive.org&lt;/code&gt;, &lt;code&gt;wikimedia.org&lt;/code&gt;, &lt;code&gt;digitalocean.com&lt;/code&gt;, &lt;code&gt;squareup.com&lt;/code&gt;, &lt;code&gt;uber.com&lt;/code&gt;, &lt;code&gt;cdc.gov&lt;/code&gt;, &lt;code&gt;go.dev&lt;/code&gt;, &lt;code&gt;rust-lang.org&lt;/code&gt; and &lt;code&gt;developer.mozilla.org&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some of those are certainly JavaScript-rendered and will have markup in a browser. But &lt;code&gt;wikipedia.org&lt;/code&gt; is not a JavaScript application, and neither is &lt;code&gt;w3.org&lt;/code&gt; or &lt;code&gt;github.com&lt;/code&gt; — those responses were 119KB, 51KB and 576KB of ordinary server-rendered HTML with no structured data anywhere in them.&lt;/p&gt;

&lt;p&gt;The honest conclusion is narrower than "half the web has no structured data" and more useful than nothing: shipping JSON-LD is a choice that a large share of serious sites have not made, and it is not a precondition for ranking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2: 14 sites still ship markup for a retired feature
&lt;/h2&gt;

&lt;p&gt;This was the most surprising result. Google retired the sitelinks search box on &lt;strong&gt;21 November 2024&lt;/strong&gt; — the search field that used to appear under a brand's result. The markup that powered it, a &lt;code&gt;SearchAction&lt;/code&gt; on the &lt;code&gt;WebSite&lt;/code&gt; node, now produces nothing.&lt;/p&gt;

&lt;p&gt;Fourteen of the 32 sites with JSON-LD still carry it: &lt;code&gt;cnn.com&lt;/code&gt;, &lt;code&gt;forbes.com&lt;/code&gt;, &lt;code&gt;walmart.com&lt;/code&gt;, &lt;code&gt;atlassian.com&lt;/code&gt;, &lt;code&gt;zoom.us&lt;/code&gt;, &lt;code&gt;pinterest.com&lt;/code&gt;, &lt;code&gt;docker.com&lt;/code&gt;, &lt;code&gt;python.org&lt;/code&gt;, &lt;code&gt;screamingfrog.co.uk&lt;/code&gt;, &lt;code&gt;yoast.com&lt;/code&gt;, &lt;code&gt;cloudflare.com&lt;/code&gt;, &lt;code&gt;heroku.com&lt;/code&gt;, &lt;code&gt;airbnb.com&lt;/code&gt; and &lt;code&gt;who.int&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two of those are SEO tool vendors. That is the interesting part: this is not a case of amateurs leaving stale markup around. It is a case of markup that was correct when it was written, that nothing broke when it stopped mattering, and that no tool flags because it is still valid schema.org. There is no error, no warning, and no rich result — just a few lines of JSON that no longer do anything.&lt;/p&gt;

&lt;p&gt;It is worth being fair about this: leaving it in place costs nothing measurable. We are not suggesting the markup is harmful. The point is that it is invisible dead weight, and the only way to notice is to know the feature was retired — which is exactly the kind of thing that a site audit will never tell you.&lt;/p&gt;

&lt;p&gt;If you are auditing your own markup, this is the check that pays: &lt;strong&gt;is any of it aimed at a feature that no longer exists?&lt;/strong&gt; The &lt;a href="https://www.serpprism.com/tools/schema-markup-generator" rel="noopener noreferrer"&gt;schema generator&lt;/a&gt; on this site does not emit &lt;code&gt;SearchAction&lt;/code&gt; for that reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3: a single block is the norm, and &lt;a class="mentioned-user" href="https://dev.to/graph"&gt;@graph&lt;/a&gt; is the minority
&lt;/h2&gt;

&lt;p&gt;Of the 32 sites with markup, 24 used exactly one block, four used two, three used three, and one used four. So the "many small blocks" pattern that &lt;a href="https://www.serpprism.com/guides/json-ld-graph-structure" rel="noopener noreferrer"&gt;our @graph guide&lt;/a&gt; warns about is not actually the common shape — one block is.&lt;/p&gt;

&lt;p&gt;Within that single block, however, the split matters. Only &lt;strong&gt;13 of the 32 (41%)&lt;/strong&gt; use a &lt;code&gt;@graph&lt;/code&gt; array. The rest put a single node at the top level.&lt;/p&gt;

&lt;p&gt;The 13: &lt;code&gt;gitlab.com&lt;/code&gt;, &lt;code&gt;atlassian.com&lt;/code&gt;, &lt;code&gt;zoom.us&lt;/code&gt;, &lt;code&gt;asana.com&lt;/code&gt;, &lt;code&gt;docker.com&lt;/code&gt;, &lt;code&gt;twitch.tv&lt;/code&gt;, &lt;code&gt;moz.com&lt;/code&gt;, &lt;code&gt;screamingfrog.co.uk&lt;/code&gt;, &lt;code&gt;yoast.com&lt;/code&gt;, &lt;code&gt;searchenginejournal.com&lt;/code&gt;, &lt;code&gt;stripe.com&lt;/code&gt;, &lt;code&gt;netlify.com&lt;/code&gt; and &lt;code&gt;heroku.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Look at that list again. Four of the thirteen are SEO tool vendors or SEO publications — Moz, Screaming Frog, Yoast and Search Engine Journal. When the people who build the tooling converge on a pattern, that is usually worth more than a spec reading, and it is consistent with the argument in the &lt;a class="mentioned-user" href="https://dev.to/graph"&gt;@graph&lt;/a&gt; guide: one connected graph beats several disconnected blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 4: most markup is boilerplate
&lt;/h2&gt;

&lt;p&gt;Twenty of the 32 sites ship only generic node types — &lt;code&gt;Organization&lt;/code&gt;, &lt;code&gt;WebSite&lt;/code&gt;, &lt;code&gt;WebPage&lt;/code&gt;, &lt;code&gt;ContactPoint&lt;/code&gt;, &lt;code&gt;PostalAddress&lt;/code&gt; — with nothing specific to the page being viewed.&lt;/p&gt;

&lt;p&gt;The type frequency across all 32 tells the same story. The most common types were &lt;code&gt;PostalAddress&lt;/code&gt; (33 occurrences), &lt;code&gt;Organization&lt;/code&gt; (25), &lt;code&gt;Place&lt;/code&gt; (24), &lt;code&gt;WebSite&lt;/code&gt; (20) and &lt;code&gt;ImageObject&lt;/code&gt; (20). &lt;code&gt;BreadcrumbList&lt;/code&gt; appeared 6 times. &lt;code&gt;SoftwareApplication&lt;/code&gt; 6 times. &lt;code&gt;VideoObject&lt;/code&gt; 6 times.&lt;/p&gt;

&lt;p&gt;More addresses than web pages. For a homepage that is defensible — the homepage is where the organization describes itself. But it means that on most of these sites, the structured data says "here is a company" and never says "here is what this particular page is about".&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 5: the pattern at full size
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;stripe.com&lt;/code&gt; packs &lt;strong&gt;54 nodes into a single &lt;code&gt;@graph&lt;/code&gt;&lt;/strong&gt; — by a wide margin the largest we found, and more than the next three sites combined.&lt;/p&gt;

&lt;p&gt;That is the &lt;a class="mentioned-user" href="https://dev.to/graph"&gt;@graph&lt;/a&gt; pattern doing what it is for. Fifty-four connected nodes with &lt;code&gt;@id&lt;/code&gt; references resolve to one coherent description of the site rather than fifty-four independent assertions that must agree with each other by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we did not find: almost no dangling references
&lt;/h2&gt;

&lt;p&gt;Our &lt;a href="https://www.serpprism.com/guides/json-ld-graph-structure" rel="noopener noreferrer"&gt;@graph guide&lt;/a&gt; warns that referencing an &lt;code&gt;@id&lt;/code&gt; which is never defined causes the property to be silently ignored. We expected to find this in the wild.&lt;/p&gt;

&lt;p&gt;One site out of 32 — &lt;code&gt;paypal.com&lt;/code&gt;, with a single reference to &lt;code&gt;https://www.paypal.com/c2/home#website&lt;/code&gt; that no node defines.&lt;/p&gt;

&lt;p&gt;Two other things we expected and did not find: &lt;strong&gt;zero&lt;/strong&gt; JSON parse failures across every block we extracted, and no site shipping markup in more than four blocks. Invalid JSON-LD and block sprawl both turn out to be much rarer than the advice would suggest.&lt;/p&gt;

&lt;p&gt;This is the second time in two surveys that a widely repeated warning did not appear at all in a sample of this size. That is not an argument for ignoring the warnings — a dangling reference is still a bug — but it is an argument for checking how often the thing you are worried about actually happens before you spend a week refactoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check on your own markup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is any of it aimed at a retired feature?&lt;/strong&gt; &lt;code&gt;SearchAction&lt;/code&gt; is the one to search for today. Check the markup against the current list of supported rich results rather than against a tutorial you followed in 2023.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it say anything about this page?&lt;/strong&gt; If every page on your site emits the same &lt;code&gt;Organization&lt;/code&gt; and &lt;code&gt;WebSite&lt;/code&gt; nodes and nothing else, the markup is describing your company and not your content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it one connected graph or several disconnected blocks?&lt;/strong&gt; Neither is an error. One is easier to keep consistent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does every &lt;code&gt;@id&lt;/code&gt; you reference exist on the page?&lt;/strong&gt; Extract the defined &lt;code&gt;@id&lt;/code&gt; values and the referenced ones and compare the lists. It takes a minute by eye on a page with under ten nodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;The sample is 61 reachable homepages chosen by hand to span categories — representative of well-known sites, not of the web. Homepages only: article, product and FAQ markup lives on inner pages and is not measured here.&lt;/p&gt;

&lt;p&gt;The server-rendered caveat is the big one. Any JavaScript-injected markup is invisible to this method, so the true rate of sites using structured data is certainly higher than 52%. The findings about &lt;code&gt;@graph&lt;/code&gt;, node types and &lt;code&gt;SearchAction&lt;/code&gt; are drawn only from markup we actually retrieved, so they are unaffected — but the headline number is a floor, not a rate.&lt;/p&gt;

&lt;p&gt;Finally, this is a single-day snapshot of pages that change constantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;The extraction script and the domain list ship with this site and run in about two minutes. It writes the extracted JSON-LD for every domain, so each claim above can be checked against the markup itself rather than our summary of it.&lt;/p&gt;

&lt;p&gt;If a row is wrong, the markup settles it. Corrections via the &lt;a href="https://www.serpprism.com/contact" rel="noopener noreferrer"&gt;contact page&lt;/a&gt; — and if you know why the sitelinks search box markup is still so widespread two years after retirement, we would like to hear that too.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
    </item>
    <item>
      <title>Link rel in the Wild: 54 Homepages</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Tue, 22 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/link-rel-in-the-wild-54-homepages-1ol2</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/link-rel-in-the-wild-54-homepages-1ol2</guid>
      <description>&lt;h2&gt;
  
  
  How this was measured
&lt;/h2&gt;

&lt;p&gt;On 2026-10-16 we requested the homepage of each of the same 78 hand-picked domains our other surveys use — news, commerce, SaaS, developer tooling, social, streaming, reference, SEO, education, finance, cloud, local and government — and parsed every &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag in the HTML we got back. Each request identified itself as a survey crawler and was attempted directly first, then through a local proxy; &lt;strong&gt;56 domains answered on the direct path and 22 needed the proxy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;63 of 78 returned a readable homepage&lt;/strong&gt; (80.8%). The other 15 are counted as undecidable, not as absent: 202 from &lt;code&gt;amazon.com&lt;/code&gt; and &lt;code&gt;booking.com&lt;/code&gt;, 403 from &lt;code&gt;ebay.com&lt;/code&gt;, &lt;code&gt;stackoverflow.com&lt;/code&gt;, &lt;code&gt;npmjs.com&lt;/code&gt;, &lt;code&gt;yelp.com&lt;/code&gt; and &lt;code&gt;nih.gov&lt;/code&gt;, 406 from &lt;code&gt;uber.com&lt;/code&gt;, and no response at all from &lt;code&gt;nytimes.com&lt;/code&gt;, &lt;code&gt;reuters.com&lt;/code&gt;, &lt;code&gt;washingtonpost.com&lt;/code&gt;, &lt;code&gt;etsy.com&lt;/code&gt;, &lt;code&gt;quora.com&lt;/code&gt;, &lt;code&gt;searchengineland.com&lt;/code&gt; and &lt;code&gt;coinbase.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Nine of the 63 then had to be excluded for a different reason: their HTML contained &lt;strong&gt;fewer than 20 links&lt;/strong&gt; , which means what we fetched was a JavaScript shell rather than a page — &lt;code&gt;reddit.com&lt;/code&gt; 0, &lt;code&gt;pinterest.com&lt;/code&gt; 18, &lt;code&gt;tumblr.com&lt;/code&gt; 4, &lt;code&gt;youtube.com&lt;/code&gt; 14, &lt;code&gt;twitch.tv&lt;/code&gt; 0, &lt;code&gt;archive.org&lt;/code&gt; 0, &lt;code&gt;khanacademy.org&lt;/code&gt; 0, &lt;code&gt;paypal.com&lt;/code&gt; 9 and &lt;code&gt;airbnb.com&lt;/code&gt; 14. &lt;strong&gt;That leaves 54 homepages carrying 10,121 anchors&lt;/strong&gt; , median 171 links each, ranging from 20 to 649.&lt;/p&gt;

&lt;p&gt;Four parsing decisions shaped every number below, and each one changed an answer. &lt;strong&gt;Internal versus external is decided by registrable domain, not by hostname&lt;/strong&gt; : &lt;code&gt;docs.docker.com&lt;/code&gt; and &lt;code&gt;www.docker.com&lt;/code&gt; are one site, and &lt;code&gt;screamingfrog.co.uk&lt;/code&gt; has a three-label registrable domain, not two. &lt;strong&gt;Non-HTTP schemes are excluded before anything is counted&lt;/strong&gt; — 144 in-page fragments, 37 anchors with no href at all, 8 &lt;code&gt;mailto:&lt;/code&gt;, 8 &lt;code&gt;tel:&lt;/code&gt; and 5 &lt;code&gt;javascript:&lt;/code&gt; links are not outbound links. &lt;strong&gt;Attribute values do not have to be quoted&lt;/strong&gt; , and a parser that insists on quotes silently discards them. And &lt;strong&gt;when a tag carries two &lt;code&gt;rel&lt;/code&gt; attributes, only the first one counts&lt;/strong&gt; , which is the HTML rule and turns out to matter here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 1: the attribute everybody argues about is the one nobody uses
&lt;/h2&gt;

&lt;p&gt;Of the 1,087 outbound links across these 54 homepages, &lt;strong&gt;41 carry &lt;code&gt;rel="nofollow"&lt;/code&gt;&lt;/strong&gt; — 3.8%. More telling is the site-level count: &lt;strong&gt;5 of 54 homepages nofollow a single outbound link&lt;/strong&gt;. Those five are &lt;code&gt;ahrefs.com&lt;/code&gt; (17 of its 37 outbound links), &lt;code&gt;digitalocean.com&lt;/code&gt; (9 of 12), &lt;code&gt;shopify.com&lt;/code&gt; (7 of 29), &lt;code&gt;semrush.com&lt;/code&gt; (6 of 10) and &lt;code&gt;x.com&lt;/code&gt; (2 of 2). Every other homepage — &lt;code&gt;bbc.com&lt;/code&gt;, &lt;code&gt;netflix.com&lt;/code&gt;, &lt;code&gt;cloudflare.com&lt;/code&gt;, &lt;code&gt;stripe.com&lt;/code&gt;, &lt;code&gt;wikipedia.org&lt;/code&gt;, &lt;code&gt;cdc.gov&lt;/code&gt; — links out to other domains with nothing on the tag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;20 of 54 homepages put no &lt;code&gt;rel&lt;/code&gt; on any outbound link at all&lt;/strong&gt; , and one, &lt;code&gt;spotify.com&lt;/code&gt;, has no outbound links on its homepage whatsoever. If you have been told that marking external links is standard practice, this sample says otherwise: it is a practice a minority of sites apply, unevenly, and three of the five that do apply it are SEO or marketing companies.&lt;/p&gt;

&lt;p&gt;The pooled percentages are almost useless here, and that is worth stating plainly. &lt;code&gt;zoom.us&lt;/code&gt; alone contributes 270 of the 1,087 outbound links, 262 of them pointing at &lt;code&gt;zoom.com&lt;/code&gt;. A single site's link structure can move any pooled number by a quarter, which is why every rate in this article is reported per site as well as pooled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2: sponsored has one user, and it is not an affiliate site
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Exactly one homepage in 54 uses &lt;code&gt;rel="sponsored"&lt;/code&gt;, and it is &lt;code&gt;nodejs.org&lt;/code&gt;.&lt;/strong&gt; It uses it eight times, on eight sponsor buttons — &lt;code&gt;digitalocean.com&lt;/code&gt;, &lt;code&gt;nodesource.com&lt;/code&gt;, &lt;code&gt;vercel.com&lt;/code&gt;, &lt;code&gt;herodevs.com&lt;/code&gt;, &lt;code&gt;cloudflare.com&lt;/code&gt;, &lt;code&gt;tuxcare.com&lt;/code&gt;, &lt;code&gt;macstadium.com&lt;/code&gt; and &lt;code&gt;microsoft.com&lt;/code&gt; — each carrying &lt;code&gt;rel="sponsored noopener"&lt;/code&gt;, &lt;code&gt;target="_blank"&lt;/code&gt; and a &lt;code&gt;utm_source=nodejs-website&lt;/code&gt; parameter. That is the textbook case for the attribute: paid placement, disclosed in markup, with the tracking parameters sitting right next to it.&lt;/p&gt;

&lt;p&gt;Which makes the absence everywhere else hard to read as anything but under-use. Homepages of this kind are exactly where sponsored logos and partner badges live, and 53 of 54 mark them with nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;rel="ugc"&lt;/code&gt; appears zero times.&lt;/strong&gt; That number needs a caveat before anyone quotes it: &lt;code&gt;ugc&lt;/code&gt; exists for user-generated content — comments, forum posts, profile fields — and a corporate homepage is the one page type that reliably has none. Zero here is evidence about where we looked, not about whether the attribute is used. It is the reason this survey reports the homepage rate for &lt;code&gt;ugc&lt;/code&gt; and refuses to generalise from it.&lt;/p&gt;

&lt;p&gt;One homepage also uses &lt;code&gt;rel="external"&lt;/code&gt; (&lt;code&gt;mozilla.org&lt;/code&gt;). It is not something Google reads; it is a convention, sometimes used by scripts that style or handle outbound links.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3: what sites actually write is noopener, and it stopped being an SEO decision
&lt;/h2&gt;

&lt;p&gt;The most common token by a wide margin is not an SEO signal at all. &lt;strong&gt;&lt;code&gt;noopener&lt;/code&gt; appears on 299 outbound links across 26 of the 54 homepages&lt;/strong&gt; ; &lt;code&gt;noreferrer&lt;/code&gt; appears on 163 across 18. The two are paired on 146 links, &lt;code&gt;noopener&lt;/code&gt; is written alone on 153, and &lt;code&gt;noreferrer&lt;/code&gt; alone on 17.&lt;/p&gt;

&lt;p&gt;None of that is about ranking — it is the &lt;code&gt;window.opener&lt;/code&gt; tabnabbing fix, and it is now largely redundant. MDN's documentation for &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; states that setting &lt;code&gt;target="_blank"&lt;/code&gt; &lt;em&gt;implicitly provides the same rel behaviour as setting &lt;code&gt;rel="noopener"&lt;/code&gt;&lt;/em&gt; in newer browser versions. &lt;strong&gt;The implicit behaviour does not extend to &lt;code&gt;noreferrer&lt;/code&gt;&lt;/strong&gt; , so if the point is to keep the referring URL out of the request, that still has to be written explicitly.&lt;/p&gt;

&lt;p&gt;Which reframes the 181 links in this sample that open a new tab without either token. &lt;strong&gt;752 links across 42 homepages carry &lt;code&gt;target="_blank"&lt;/code&gt;; 181 of them, on 23 sites, have no &lt;code&gt;noopener&lt;/code&gt; and no &lt;code&gt;noreferrer&lt;/code&gt;&lt;/strong&gt; , and 58 of those 181 point off-site. On a modern browser that is a non-issue. On an old one, or if you wanted &lt;code&gt;noreferrer&lt;/code&gt;, it is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 4: two sites write noopener in a form the browser cannot read
&lt;/h2&gt;

&lt;p&gt;Counting tokens rather than trusting the attribute is what surfaced these, and both were verified against the raw HTML before being written down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;screamingfrog.co.uk&lt;/code&gt; writes &lt;code&gt;rel=”noopener”&lt;/code&gt; with typographic quotation marks&lt;/strong&gt; — U+201D, the curly character a word processor or a copy-paste from a rich-text editor produces — on four social links in its footer. The value is not the token &lt;code&gt;noopener&lt;/code&gt;; it is a nine-character string containing it. The browser parses the attribute fine and then matches nothing. This is an SEO software vendor's own site, and it is the kind of defect no validator flags, because the markup is valid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;moz.com&lt;/code&gt; writes two &lt;code&gt;rel&lt;/code&gt; attributes on the same tag&lt;/strong&gt; , three times: &lt;code&gt;&amp;lt;a rel="author" href="…" target="_blank" rel="noopener noreferrer"&amp;gt;&lt;/code&gt;. HTML takes the first and ignores the second, so those three links open a new tab with &lt;code&gt;rel="author"&lt;/code&gt; and nothing else. The fix is one attribute with all three tokens, and the reason it went unnoticed is that the HTML looks correct to a human reader.&lt;/p&gt;

&lt;p&gt;Two smaller ones in the same family: &lt;strong&gt;145 links across &lt;code&gt;atlassian.com&lt;/code&gt;, &lt;code&gt;slack.com&lt;/code&gt; and &lt;code&gt;developer.mozilla.org&lt;/code&gt; carry &lt;code&gt;rel=""&lt;/code&gt;&lt;/strong&gt; — an empty value, almost certainly a framework rendering an empty prop. It does nothing. And &lt;code&gt;harvard.edu&lt;/code&gt; uses &lt;code&gt;rel="home"&lt;/code&gt; on its logo link, a value that has been obsolete for years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 5: five sites put nofollow on their own links
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;43 internal links across 5 homepages carry &lt;code&gt;rel="nofollow"&lt;/code&gt;&lt;/strong&gt; — links pointing at the same registrable domain they appear on. The counts: &lt;code&gt;digitalocean.com&lt;/code&gt; 16, &lt;code&gt;x.com&lt;/code&gt; 14, &lt;code&gt;docker.com&lt;/code&gt; 5, &lt;code&gt;ahrefs.com&lt;/code&gt; 4 and &lt;code&gt;stanford.edu&lt;/code&gt; 4.&lt;/p&gt;

&lt;p&gt;Read the actual tags and the pattern splits in two. &lt;code&gt;digitalocean.com&lt;/code&gt; nofollows footer furniture — &lt;code&gt;/press&lt;/code&gt;, &lt;code&gt;/leadership/executive-management&lt;/code&gt;, &lt;code&gt;/currents&lt;/code&gt;, &lt;code&gt;/open-source&lt;/code&gt;, plus &lt;code&gt;store.digitalocean.com&lt;/code&gt;, &lt;code&gt;status.digitalocean.com&lt;/code&gt; and &lt;code&gt;ideas.digitalocean.com&lt;/code&gt;. &lt;code&gt;x.com&lt;/code&gt; nofollows its own legal and help pages — &lt;code&gt;/tos&lt;/code&gt;, &lt;code&gt;/privacy&lt;/code&gt;, &lt;code&gt;help.x.com&lt;/code&gt;, &lt;code&gt;about.x.com&lt;/code&gt;, &lt;code&gt;careers.x.com&lt;/code&gt;, each with &lt;code&gt;rel="noopener noreferrer nofollow"&lt;/code&gt;. &lt;code&gt;docker.com&lt;/code&gt; nofollows its own installer downloads, &lt;code&gt;ahrefs.com&lt;/code&gt; nofollows &lt;code&gt;help.ahrefs.com&lt;/code&gt; and its own &lt;code&gt;/podcast&lt;/code&gt;, and &lt;code&gt;stanford.edu&lt;/code&gt; nofollows its copyright, trademark and non-discrimination pages.&lt;/p&gt;

&lt;p&gt;Some of that is defensible — nofollow on a login or a legal page is a way of saying "do not spend crawl budget here" — and some of it looks like a template applied to a whole footer. Either way it is a decision being made about internal crawl paths, and it is being made on five of the most heavily engineered homepages in the sample rather than by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to ship
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stop nofollowing outbound links by default.&lt;/strong&gt; Five of 54 homepages do it at all, 20 mark nothing, and Google has treated these values as hints rather than directives since 2019. Marking every external link was never the norm this sample reflects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do use &lt;code&gt;sponsored&lt;/code&gt; where money changed hands.&lt;/strong&gt; &lt;code&gt;nodejs.org&lt;/code&gt; is the only site here that does, and it is the clearest, cheapest signal available: one attribute on the links you are already tagging with UTM parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put &lt;code&gt;ugc&lt;/code&gt; where the user-generated content is.&lt;/strong&gt; Comment templates, forum threads, review forms — not the homepage. Zero of 54 homepages use it because none of them have comments on the homepage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep &lt;code&gt;noopener&lt;/code&gt; only if you need old-browser coverage, and treat &lt;code&gt;noreferrer&lt;/code&gt; as the part that still counts.&lt;/strong&gt; Modern browsers imply the former for &lt;code&gt;target="_blank"&lt;/code&gt;; they do not imply the latter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audit for the two broken patterns, because no tool warns you about either.&lt;/strong&gt; Grep the rendered HTML for curly quotes inside attribute values, and for tags with &lt;code&gt;rel&lt;/code&gt; twice. Both are valid HTML that does nothing — &lt;code&gt;grep -o 'rel=[^ &amp;gt;]*' page.html | sort | uniq -c | sort -rn&lt;/code&gt; shows the first in one line.&lt;/p&gt;

&lt;p&gt;To write the head tags for a new page without any of them drifting apart, the &lt;a href="https://www.serpprism.com/tools/meta-tag-generator" rel="noopener noreferrer"&gt;meta tag generator&lt;/a&gt; produces the title, description, canonical and robots tag from one input, and the &lt;a href="https://www.serpprism.com/tools/robots-txt-tester" rel="noopener noreferrer"&gt;robots.txt tester&lt;/a&gt; covers the file that governs crawling rather than link-level signals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Homepages only, and only 54 of them.&lt;/strong&gt; A homepage is where navigation and footer link sets dominate: the median homepage here has 171 links and only 13 outbound ones, and outbound links are a median 8.2% of everything on the page. Article pages, category pages and comment threads have completely different link profiles, and &lt;code&gt;ugc&lt;/code&gt; in particular lives there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-rendered HTML only.&lt;/strong&gt; Nine otherwise-readable homepages had to be dropped because their HTML was a JavaScript shell, and any link added after load is invisible to this method. The sample is biased towards sites that render on the server, which is the same bias our &lt;a href="https://www.serpprism.com/guides/heading-structure-in-the-wild" rel="noopener noreferrer"&gt;heading&lt;/a&gt; and &lt;a href="https://www.serpprism.com/guides/favicon-in-the-wild" rel="noopener noreferrer"&gt;favicon&lt;/a&gt; surveys carry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The internal/external split is mechanical.&lt;/strong&gt; We compare registrable domains, so &lt;code&gt;zoom.us&lt;/code&gt; linking to 262 &lt;code&gt;zoom.com&lt;/code&gt; URLs is counted as outbound even though it is one company, and a link to a site's own CDN or a subsidiary counts as external. That single site contributes a quarter of the pooled outbound total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One snapshot, one user agent.&lt;/strong&gt; Everything was fetched on 2026-10-16 with a crawler identity. WAFs answer differently per client, and while every verdict in this article was checked against the raw tag, the pages themselves were not re-requested with a browser user agent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We counted attributes, not intent.&lt;/strong&gt; A &lt;code&gt;rel&lt;/code&gt; that a framework renders but a script strips, or one added client-side, lands in the wrong bucket, and we cannot tell from HTML which links anyone considers paid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;The script and the shared domain list ship with this site. &lt;code&gt;node scripts/survey-link-rel.mjs&lt;/code&gt; fetches all 78 homepages and stores them; &lt;code&gt;--report&lt;/code&gt; re-parses and re-derives every number from the stored HTML without touching the network, which is how the parsing fixes were applied after the fetch; &lt;code&gt;--evidence&lt;/code&gt; prints the original &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag behind every verdict — that is what confirmed the curly-quoted &lt;code&gt;noopener&lt;/code&gt;, the duplicated &lt;code&gt;rel&lt;/code&gt; and all eight &lt;code&gt;sponsored&lt;/code&gt; links. Our &lt;a href="https://www.serpprism.com/guides/robots-txt-in-the-wild" rel="noopener noreferrer"&gt;robots.txt&lt;/a&gt;, &lt;a href="https://www.serpprism.com/guides/canonical-in-the-wild" rel="noopener noreferrer"&gt;canonical&lt;/a&gt;, &lt;a href="https://www.serpprism.com/guides/ai-crawlers-in-the-wild" rel="noopener noreferrer"&gt;AI crawler&lt;/a&gt; and &lt;a href="https://www.serpprism.com/guides/indexing-directives-in-the-wild" rel="noopener noreferrer"&gt;indexing directive&lt;/a&gt; surveys run the same domain list from other angles.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>technicalseo</category>
      <category>linking</category>
      <category>html</category>
    </item>
    <item>
      <title>Why Google Won't Index Your Page: A Checklist</title>
      <dc:creator>任宏涛</dc:creator>
      <pubDate>Tue, 22 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/_6614b04c8831704fcf9b2/why-google-wont-index-your-page-a-checklist-94d</link>
      <guid>https://dev.to/_6614b04c8831704fcf9b2/why-google-wont-index-your-page-a-checklist-94d</guid>
      <description>&lt;h2&gt;
  
  
  Two problems that look identical and are not
&lt;/h2&gt;

&lt;p&gt;Almost every "my page is not indexed" complaint is one of two things, and they need opposite responses.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;code fault&lt;/strong&gt; is something wrong with the URL or the page. It is fixable today, and once it is fixed the page becomes eligible. A &lt;strong&gt;trust gate&lt;/strong&gt; is Google having fetched the page and decided not to spend index space on it yet. There is nothing to fix, because nothing is broken — and this is where most advice sends people in circles.&lt;/p&gt;

&lt;p&gt;Order matters because the two produce the same symptom in Search Console, and the fix for one does nothing for the other. Re-requesting indexing on a page in a trust gate wastes your one lever. Rewriting the content of a page blocked by a &lt;code&gt;noindex&lt;/code&gt; tag is worse than wasted, because the actual problem is still sitting there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — read the status code, not the page
&lt;/h2&gt;

&lt;p&gt;A page that renders in your browser is not proof of anything. Ask the server instead:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -sS -o /dev/null -w "%{http_code}" https://example.com/page&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;What the answer tells you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;404 or 410&lt;/strong&gt; — the page is not indexed by design. Either the URL is wrong or it was deliberately removed. Nothing else in this guide applies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5xx&lt;/strong&gt; — the server is failing. Google backs off and retries later; it will not index a page it cannot fetch. Fix the server first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;301 or 302&lt;/strong&gt; — you are looking at a different URL from the one you think. Follow the redirect and diagnose the destination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;200&lt;/strong&gt; — good, but keep reading, because 200 is where the real traps live.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trap that hides inside a 200 is the &lt;strong&gt;soft 404&lt;/strong&gt; : the server reports success and the content is an error page. Our &lt;a href="https://www.serpprism.com/guides/robots-txt-in-the-wild" rel="noopener noreferrer"&gt;robots.txt survey&lt;/a&gt; caught exactly this on two of 78 well-known domains — &lt;code&gt;khanacademy.org&lt;/code&gt; and &lt;code&gt;cdc.gov&lt;/code&gt; answered 200 with an HTML error page instead of a robots file. A monitor watching status codes sees nothing wrong and a browser shows a page. If your unindexed URL returns 200 and reads like an error, you have your answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — is it blocked?
&lt;/h2&gt;

&lt;p&gt;Two mechanisms can block indexing, and they are not the same mechanism:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;robots.txt&lt;/strong&gt; — a &lt;code&gt;Disallow&lt;/code&gt; line stops the crawl. Note that it stops the &lt;em&gt;fetch&lt;/em&gt;, not the indexing of a URL Google already knows about from links elsewhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;noindex&lt;/code&gt; meta tag or &lt;code&gt;X-Robots-Tag&lt;/code&gt; header&lt;/strong&gt; — this stops indexing even when the page is crawled normally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One combination costs people weeks: &lt;strong&gt;a robots.txt &lt;code&gt;Disallow&lt;/code&gt; and a &lt;code&gt;noindex&lt;/code&gt; on the same page.&lt;/strong&gt; The disallow prevents Google from fetching the page, so Google never sees the &lt;code&gt;noindex&lt;/code&gt;, so the URL can still be indexed from links pointing at it. You then remove the &lt;code&gt;noindex&lt;/code&gt;, observe no change, and conclude the tag was never the problem. The order to undo it is: remove the &lt;code&gt;Disallow&lt;/code&gt; first, let Google fetch the page, confirm it reads the &lt;code&gt;noindex&lt;/code&gt;, and only then remove that.&lt;/p&gt;

&lt;p&gt;You can test the first of these with our &lt;a href="https://www.serpprism.com/tools/robots-txt-tester" rel="noopener noreferrer"&gt;robots.txt tester&lt;/a&gt;. For the header, one curl is enough: &lt;code&gt;curl -sSI https://example.com/page | grep -i x-robots-tag&lt;/code&gt;. And note that a page with no robots meta at all is normal — &lt;code&gt;index, follow&lt;/code&gt; is the default, so its absence is not a fault.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — can it be discovered?
&lt;/h2&gt;

&lt;p&gt;Google cannot index a URL it never finds. Three ways a page stays invisible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It is not in your sitemap.&lt;/strong&gt; Check that the URL appears in &lt;code&gt;sitemap.xml&lt;/code&gt; — and that the URL listed there actually exists. A sitemap full of 404s is a signal of neglect rather than of coverage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The sitemap is not declared.&lt;/strong&gt; Listing it in robots.txt lets a crawler find it without being told twice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing links to it.&lt;/strong&gt; This is the most common cause of a page that is technically perfect and never gets indexed. A page with no internal links is an orphan, and orphans depend entirely on the sitemap being read.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is the same class of omission our &lt;a href="https://www.serpprism.com/guides/canonical-in-the-wild" rel="noopener noreferrer"&gt;canonical survey&lt;/a&gt; found on 11 of 55 famous homepages: the tag was simply absent. Nobody forgot on purpose. Omission is the default state of a site nobody audits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — is it a duplicate?
&lt;/h2&gt;

&lt;p&gt;If the page is crawlable, discoverable and unblocked, the next question is whether Google treats it as a second copy of something else. The canonical tag is the instruction that decides this, and the mistake that matters is comparing it against the wrong URL.&lt;/p&gt;

&lt;p&gt;In our canonical survey, 43 of 55 homepages redirected at least once before serving content. If you compare your canonical against the URL you typed rather than the URL you were finally served, you will conclude you have a cross-domain canonical problem when you do not. Compare against the final URL after redirects. Then check that the canonical target itself returns 200 — two of the 43 we tested pointed at URLs that did not, and nothing warns you when that happens.&lt;/p&gt;

&lt;p&gt;Three more duplicates worth ruling out: the http and https versions of the page, the &lt;code&gt;www&lt;/code&gt; and non-&lt;code&gt;www&lt;/code&gt; versions, and any tracking parameters that create a second address for the same content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — is the content actually there?
&lt;/h2&gt;

&lt;p&gt;If the page is a single-page app, the HTML Google receives may contain almost nothing. Curl the URL and read the raw source rather than the rendered page. If the body is an empty container and a bundle of script tags, that is what a crawler may be evaluating.&lt;/p&gt;

&lt;p&gt;This is not hypothetical. Two of the homepages in our &lt;a href="https://www.serpprism.com/guides/serp-snippet-in-the-wild" rel="noopener noreferrer"&gt;SERP snippet survey&lt;/a&gt; returned client-rendered shells with no title in the served HTML at all. Google can render JavaScript, but it does so on a second pass, and a page that needs rendering is a page that is slower and less certain to be evaluated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6 — now it is a trust gate, and there is nothing to fix
&lt;/h2&gt;

&lt;p&gt;If every check above passed, the page is eligible and Google is choosing not to index it yet. Search Console describes this in two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discovered — currently not indexed&lt;/strong&gt; : Google knows the URL exists and has not fetched it yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crawled — currently not indexed&lt;/strong&gt; : Google fetched it and judged it not worth index space at this point.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither is a defect, and neither is a bug you can patch. The usual reasons are thin content, no internal links, a brand-new domain with no authority, or content close enough to pages that already exist that Google does not need another copy of it.&lt;/p&gt;

&lt;p&gt;What actually moves it: content clearly more useful than what is already indexed, internal links from pages that are themselves indexed, genuine external links or mentions, and time. What does not move it: requesting indexing repeatedly, resubmitting the sitemap repeatedly, or rewriting the page for the third time.&lt;/p&gt;

&lt;p&gt;We went through this on our own site. A batch of pages sat in "Crawled — currently not indexed" while every technical check passed: 200 status, self-referencing canonical, index and follow, well over a thousand words each. There was nothing to fix. They drained on their own within days. The useful conclusion is not "wait and hope" — it is that &lt;strong&gt;when all five steps above pass, the correct action is to stop working on that page and go make the rest of the site better.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Request indexing" does, and does not do
&lt;/h2&gt;

&lt;p&gt;It queues a fetch. That is the whole of it. It is worth using on a brand-new URL, or on a page you have just fixed, because it shortens the wait for the next crawl.&lt;/p&gt;

&lt;p&gt;It is not worth using on a page that has already been crawled and judged. Requesting indexing does not override a quality decision, there is a practical limit on how often you can use it, and spending it on a trust gate burns the one lever you have for a genuine fault.&lt;/p&gt;

&lt;h2&gt;
  
  
  The order, in one table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;If it fails&lt;/th&gt;
&lt;th&gt;Fixable by you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Status code&lt;/td&gt;
&lt;td&gt;404, 410, 5xx, or a soft 404&lt;/td&gt;
&lt;td&gt;Yes, today&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;robots.txt, noindex, X-Robots-Tag&lt;/td&gt;
&lt;td&gt;Blocked from crawl or from index&lt;/td&gt;
&lt;td&gt;Yes, today&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Sitemap and internal links&lt;/td&gt;
&lt;td&gt;Undiscoverable, or an orphan&lt;/td&gt;
&lt;td&gt;Yes, today&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Canonical and duplicates&lt;/td&gt;
&lt;td&gt;Consolidated into another URL&lt;/td&gt;
&lt;td&gt;Yes, today&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Content present in the served HTML&lt;/td&gt;
&lt;td&gt;Empty shell as seen by a crawler&lt;/td&gt;
&lt;td&gt;Yes, today&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;All of the above passed&lt;/td&gt;
&lt;td&gt;A trust gate&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;No&lt;/strong&gt; — content, links and time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The point of working in this order is that you stop at the first failure, and you stop entirely at step 6. Most people start at step 6 and work backwards, which is why the advice to "just keep improving the content" gets applied to pages that were never crawlable in the first place.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>marketing</category>
    </item>
  </channel>
</rss>
