Skip to content
pageinspection

Article schema: headline, dates and the ones people fake

There are four properties on an Article block worth arguing about, and most implementations get the same three of them wrong: a headline copied from the wrong field, a modified date that a build step regenerates, and an image nobody checked the size of. None of those raises an error anywhere.

The short answer

Article markup has two properties people get wrong. headline is not your title tag and gets truncated past about 110 characters, so it should be the article title alone. datePublished and dateModified must be real: a dateModified that advances on every deploy is the property most commonly falsified, and it is easy to detect.

ImportantAudit check · Article Schema

headline is not your title tag, and it has a ceiling

The rule almost nobody repeats: Google's guidance puts a limit of 110 characters on headline, and a longer value can be truncated or disregarded. Your title tag has no character limit, only a pixel width beyond which a results page stops drawing it, which is a completely different constraint that happens to bite at a similar length.

The two fields are also different in what they should contain. A title tag is written for a results page, so it usually carries a qualifier and often the site name. A headline is the article's own headline: the words a reader sees at the top of the piece, with no brand suffix and no category prefix. Piping one field into both is the standard shortcut and it produces headlines ending in a vertical bar and a company name.

If the real headline runs long, that is what alternativeHeadline exists for. Put the short form in headline, the full one in alternativeHeadline, and stop worrying about the count. And keep the headline the same words as the visible h1: a block claiming a headline the page does not show is describing a different article. Which field gets what, and how a title behaves once a results page has it, sit in the title tag check.

The two dates, and the one people cheat on

datePublished is when the article first became public. dateModified is when its content last meaningfully changed. Both take ISO 8601, and both should include a time and a timezone offset, because a bare date is a twenty-four hour window in an unstated zone and publication order can matter inside that window.

Then there is the trick. Everyone learns that fresher content does better on freshness-sensitive queries, and somebody proposes setting dateModified to the current date on every build, or on a nightly cron. It is trivial to implement and it is a bad trade for three separate reasons.

  • It contradicts the page. Your markup says today and your byline says March 2023. One of your own signals is now wrong, and a consumer has to pick, which is a worse position than having only the honest one.
  • It destroys the signal you wanted. If every article was modified today, the property carries no information at all. The day you genuinely rewrite something, there is no way to say so, because you already said it about four hundred pages that did not change.
  • The visible date usually wins anyway. A date in your markup is one input. What is printed in the article, what the URL implies, what the page said the last time it was crawled, and how much of the text has actually changed since are all inputs too, and the markup does not get to overrule them by asserting harder.

The version that works is unexciting: emit dateModified from the timestamp of the last real edit to the content, in the CMS or the repository, and never from the build clock. If you cannot separate an edit from a redeploy, that is the bug to fix. And if you want a page to be genuinely fresh, change it, then say so.

Your sitemap makes the same claim, from a different source.

lastmod in your XML sitemap and dateModified in your markup are two statements about one event, and on most stacks they are generated by different code reading different fields. A build step that stamps the current time into both makes them agree and both wrong, which is the failure that is hardest to notice. A build step that touches only one leaves you contradicting yourself. Wire both to the same edit timestamp and the problem disappears in one change.

Check one page for an editorial type

This reports whether the URL declares a type in the article family. The properties this guide is about live inside the block, so a pass here means the type is present and correct, not that the headline fits or the dates are true.

Check one page for Article markup

No signup required. Each free search audits one page, paste any URL to see it in action.

Three types Google treats as one

Article, NewsArticle and BlogPosting generate more deliberation than they deserve. Google documents its article features across all three and does not distinguish between them for eligibility, so choosing between them buys you nothing there. Pick the one that describes the content and move on: BlogPosting for posts, NewsArticle if you are a news publisher reporting events, Article when neither fits.

The distinction does matter in two places. Consumers other than Google are not obliged to treat the three as equivalent, and a feed reader, an aggregator or a research pipeline filtering for news will look at the type. And a handful of properties only exist on the narrower types: dateline and printEdition belong to NewsArticle and are meaningless elsewhere. Declaring NewsArticle on a marketing blog because it sounded more serious is the actual mistake here, and it is a claim about what your site is.

article.html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "@id": "https://example.com/blog/cdc-without-tears#article",
  "headline": "Change data capture without the 3am pages",
  "alternativeHeadline": "Change data capture without the 3am pages: what two years of replication lag taught us about triggers",
  "description": "Why we moved off triggers, what broke, what we kept.",
  "datePublished": "2026-03-04T09:30:00+00:00",
  "dateModified": "2026-07-19T14:05:00+01:00",
  "author": { "@id": "https://example.com/authors/jane-doe#person" },
  "publisher": { "@id": "https://example.com/#organization" },
  "image": [
    "https://example.com/img/cdc-16x9.jpg",
    "https://example.com/img/cdc-4x3.jpg",
    "https://example.com/img/cdc-1x1.jpg"
  ],
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://example.com/blog/cdc-without-tears"
  },
  "isPartOf": { "@id": "https://example.com/#website" },
  "articleSection": "Engineering",
  "inLanguage": "en",
  "wordCount": 2140
}
</script>

Both people properties are references to entities declared elsewhere rather than names repeated here, which is a check in its own right and the single biggest improvement most Article blocks can make. wordCount and articleSection are optional and cheap, and both are things a parser would otherwise have to estimate.

What not to add: articleBody containing your entire article. It is valid, it doubles the weight of your HTML, and it gives you two copies of the same text that can drift apart the moment anybody edits one of them.

The image rules that decide eligibility

image is where an otherwise complete block fails, because the requirements are about files and pixels rather than syntax, and nothing in your validator opens the file.

  • Supply more than one crop.The property accepts an array, and 16 by 9, 4 by 3 and 1 by 1 versions of the same image let a consumer choose rather than crop your subject's head off. One 16 by 9 image is acceptable and three are better.
  • Width is the constraint that bites. Large image treatment in Google Discover wants images at least 1200 pixels wide, and it also wants <meta name="robots" content="max-image-preview:large"> on the page. A perfect Article block with an 800-pixel hero and a default preview setting is not eligible, and the reason is in neither the markup nor the validator.
  • The file has to be reachable and indexable. An image behind a disallowed path, a signed URL that expires, or a hotlink-protected CDN is not an image as far as a crawler is concerned. Signed URLs are the nastiest of the three because the block is correct on the day it ships and broken a week later.
  • It should be the article's image. A fallback that resolves to your logo on every post means every article on the site is illustrated identically, which is worse than none, because it makes your entire archive look like one page.

Why honest dates only rate Important

Because the facts are already on the page. A byline, a date and a headline are visible in the rendered article, and search engines have been extracting all three from pages without markup for as long as there have been blogs. They have to: most of the web still has no Article block. Absence therefore costs you less here than on any other type in this pillar.

What the markup buys is the removal of one specific ambiguity. A typical article page shows several dates: a publication date, a comment timestamp, three related posts with their own dates, a copyright year in the footer. An explicit datePublished ends the guessing, and that is a real if narrow benefit.

It clears the bottom of the scale for a reason unusual on this list: the common failure is not omission but assertion. Nobody accidentally lies about a heading. A build step that stamps today's date on four hundred articles is a page telling a confident falsehood about itself, which is a different kind of defect from a missing property and rated accordingly. The scale that produces these labels is described on the audit page.

Confirming the template did not invent your dates

Parse the block, then measure the headline, because that is the one property with a number attached and the one no validator counts for you.

check-article.sh
# headline length, from what the page actually serves
curl -s https://example.com/blog/cdc-without-tears \
  | grep -o '"headline": *"[^"]*"' \
  | sed 's/.*: *"//; s/"$//' \
  | awk '{ print length, $0 }'

# the dates as shipped, offsets included
curl -s https://example.com/blog/cdc-without-tears \
  | grep -o '"date[A-Za-z]*": *"[^"]*"'

Then the check that matters more than either. Run those two commands against the same article today and again after your next deploy, having changed nothing in the content. If dateModified moved, your build clock is writing your editorial history, and every freshness claim your site makes is now noise. Fix that before tuning anything else in the block.

Finally, put the rendered page beside the markup and confirm four things agree: the headline against the h1, the published date against the printed one, the byline against the author reference, and the image URL against the hero that actually loaded. All four are places a template can substitute a fallback without anybody noticing, and all four are claims about content a reader can see.

Questions this check raises

What is the difference between Article, NewsArticle and BlogPosting?
Google treats all three the same way for its purposes, so the choice is about accuracy rather than about eligibility. Use NewsArticle for journalism, BlogPosting for a blog, and Article when neither fits. Picking the most specific true type is good practice; agonising over it is not.
Should dateModified change when I fix a typo?
No. dateModified means the content changed in a way a reader would care about, and using it for every deploy or every whitespace change turns it into a deploy timestamp. Publishers who do that find the field discounted, and once it is discounted a genuine update no longer registers.
How long can the headline property be?
Google truncates around 110 characters, so anything past that is discarded rather than shortened gracefully. It is also not the place for your title tag: the site name, the separator and the positioning phrase belong in the title, and headline should be the article title as it appears on the page.