Open Graph & Twitter
What SEO NEO emits by default, the og:type=article rules, the four-step Open Graph image resolver, Twitter / X card behaviour, and locale handling.
What SEO NEO emits by default
Open Graph and Twitter card tags are generated automatically from the same data as the core meta tags — you don't need to configure anything separately for most sites. The default output for a page with an image:
<meta property="og:title" content="Page Title">
<meta property="og:description" content="Resolved description.">
<meta property="og:url" content="https://example.com/page/">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Your Site Name">
<meta property="og:image" content="https://example.com/site/assets/files/123/hero.jpg">
<meta property="og:image:width" content="1600">
<meta property="og:image:height" content="900">
<meta property="og:image:secure_url" content="https://example.com/site/assets/files/123/hero.jpg">
<meta property="og:image:type" content="image/jpeg">
<meta property="og:locale" content="en_US">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Page Title">
<meta name="twitter:description" content="Resolved description.">
<meta name="twitter:image" content="https://example.com/site/assets/files/123/hero.jpg">
Open Graph tag breakdown
- og:title — the raw page title, without the separator or site name. Uses the same fallback chain as the
<title>tag. - og:description — the same resolved description as
<meta name="description">. - og:url — mirrors the resolved canonical URL, so all three tags stay in sync.
- og:type — resolves per-page via the
seoneo_og_typefield (installed by default), then per-template via anog_type=line in per-template defaults, then the site-wide default in module config (websiteif nothing is set). Hookable via___getOgType($page). The Default OG type select in module config covers the full set of common Open Graph types:website,article,book,profile,music.song,music.album,music.playlist,music.radio_station,video.movie,video.episode,video.tv_show,video.other, andproduct. - og:site_name — from the Site name setting in module config.
- og:locale — resolves per language. See Multilingual for the locale map.
- og:locale:alternate — emitted once per active language other than the current one.
Open Graph image resolver
SEO NEO uses a five-step resolver to find an image for og:image:
- Dedicated OG image field — checks
seoneo_og_imageon the current page first (installed by default with the SEO tab). - Configured image fields — scans the comma-separated list in OG image fields (default:
og_image,screenshot,images,image,blog_images). Supports dotted paths:banner.image,gallery.first. - Ancestor walk — if Inherit OG image from closest ancestor is enabled (off by default), walks
$page->parents()nearest-first using steps 1 and 2 on each ancestor. - Homepage OG image — if the homepage has
seoneo_og_imageset, uses that as a site-wide fallback. - Default OG image URL — the fallback image URL from module config. If still empty, the
og:imagefamily of tags is omitted.
When the resolved image is a real Pageimage object (i.e. a file in PW's file system, not just a URL string), SEO NEO also emits og:image:width, og:image:height, og:image:secure_url, and og:image:type automatically. Facebook silently rejects images on the first share when dimensions are missing — this is the most common cause of "no image" in link previews.
og:type=article and article tags
When og:type resolves to article, SEO NEO automatically adds three extra tags:
<meta property="article:author" content="Jane Doe">
<meta property="article:published_time" content="2026-05-12T11:49:20+01:00">
<meta property="article:modified_time" content="2026-05-14T15:37:31+01:00">
- article:author — one tag per author, from the same multi-author resolution as
<meta name="author">. Hookable via___getArticleAuthors($page). - article:published_time — defaults to the page's
$page->createdtimestamp as ISO 8601. Hookable via___getArticlePublishedTime($page). - article:modified_time — defaults to
$page->modified. Hookable via___getArticleModifiedTime($page).
These tags are only emitted when og:type=article, so non-article pages stay clean.
To set og:type=article on a template, add this to the per-template defaults in module config:
[blog-post]
og_type=article
Twitter / X cards
Twitter card tags mirror the Open Graph values — they share the same resolved title, description, and image:
- twitter:card — switches between
summary_large_image(when an image resolved) andsummary(no image). Automatic. - twitter:site — the site's Twitter/X handle from module config. The leading
@is added automatically. - twitter:creator — the author's handle from module config. Hook
___getTwitterCreator($page)to return a per-page or per-author value. - twitter:title, twitter:description, twitter:image — mirror
og:title,og:description, andog:imagerespectively. Twitter falls back to OG tags by spec, but many scrapers don't, so SEO NEO emits explicit Twitter tags for maximum compatibility.
Locale handling
On single-language sites, og:locale uses the Default OG locale from module config (e.g. en_US).
On multilingual sites, SEO NEO resolves the locale for each active language using a three-step fallback chain:
- Locale map — explicit
langname=localeentries in Modules > Configure > SeoNeo > Locale map for languages:default=en_GB de=de_DE fi=fi_FI - Auto-derive from language name + country code — if the language has no explicit map entry, SEO NEO derives a locale from the PW language name itself.
debecomesde_DE,frbecomesfr_FR,ptbecomespt_PT. Compound names likept-brmap topt_BR. - Default OG locale — final fallback, used for the
defaultlanguage and as a safety net when neither of the above resolves.
The current language's locale becomes og:locale; all others become og:locale:alternate tags. See Multilingual for full hreflang and locale configuration.
Common gotchas
- Image not showing in Facebook share preview. The most common cause is missing
og:image:widthandog:image:height. These are only emitted when the image is a realPageimage— check the image field is returning a PW file object, not just a URL string. Also confirm the image is at least 200x200px (Facebook's minimum). - og:type stays "website" even on blog posts. The type resolves per-page, then per-template. Add
blog_post og_type: articleto the per-template defaults in module config, or add anseoneo_og_typeText field to the template and set it toarticleon each page. - Twitter card shows "summary" instead of "summary_large_image". No image resolved. Check the OG image fields list in module config and verify the page (or an ancestor) has an image in one of those fields.
See also
- Configuration — OG image fields, locale map, and Twitter handle settings.
- Multilingual — locale map, hreflang, and per-language OG locale.
- Template API — accessing
$page->seoneo->ogImage,->og->render(), and other OG values in templates.
Last updated