It’s not as simple as just picking a good image
Even though Open Graph tags use an open standard, each platform (Facebook, Twitter, WhatsApp, etc) treats Open Graph tags slightly differently. Some even have their own proprietary ‘versions’ of Open Graph tagging, too. Twitter’s ‘twitter card’ markup, for example, bears a strong resemblance to, and overlaps with, Open Graph tagging.
As an open project, the Open Graph is constantly changing and improving, too. Features and support come and go, the documentation isn’t always up to date, and individual platforms make (and change) their own decisions on how they interpret it, and which bits they’ll implement.
And as the web itself continues to evolve, there are more and more ‘platforms’ where people can share content, and where the Open Graph is used. From Slack, to WeChat, to tomorrow’s productivity and social media apps, they’ll all rely on the Open Graph, but use it in subtly different ways.
So when we’re trying to define a ‘best practice’ approach to support as part of the Yoast SEO plugin, it’s not as simple as just picking the ‘best image’ – we need to make sure that we provide the right tags and image formats for each platform and network.
To make things more complex, these tags and approaches sometimes conflict with or override each other. Twitter’s twitter:image property, for example, overrides an og:image value for images shared via Twitter, when both sets of tags are on the same page.
Lastly, the open graph specification allows us to provide multiple og:image values. This, in theory, allows the platform to make the best decision about which size to use and allows people who are sharing some choice over which image they pick. How different platforms interpret these values, however, varies considerably.
This logic behind which platforms use which images, in which scenarios, gets complex pretty quickly! So more often than not, we’re stuck relying on the og:image value(s) as a general default for all platforms, and adding specific support where we can, whilst trying to minimize conflict. This doesn’t always produce perfect results, so we’re always on the lookout for better ways to ‘get it right’ without requiring end users to spend hours specifying multiple image formats for each post they write.
The challenge of og:image as a default
In a perfect world, there are two different approaches to how platforms handle Open Graph tagging. They look like this:
- All platforms only use og:image tags. When multiple images are set, they automatically select (and crop) the best version for their context.
- All platforms have specific Open Graph tags (or their own versions). They allow fine-grain control over every scenario, by enabling us to specify the exact image which should be used in each case.
Unfortunately, we’re stuck somewhere in-between. Some platforms allow a degree of control, but the og:image tag functions as a general fallback for all other scenarios.
This is particularly problematic, as the og:image is also Facebook’s primary image. This is a huge challenge for the Yoast SEO plugin team, and for anybody else trying to define a ‘best practice’ approach to tagging. The image we specify as the main image for Facebook sharing (usually a large, high-res picture) also has to be suitable as a general default for all platforms which don’t have their own specific tags.
For many of these platforms, a large file size optimized for sharing in a Facebook newsfeed won’t be appropriate for their context. For example, Pinterest expects a relatively small, square cropped thumbnail image when sharing from a page – and whilst it has its own tagging mechanisms, the presence of an og:image tag on the page overrides those.
There’s more complexity, too. Different platforms have varying restrictions on image dimensions, ratios, and file size. A high-res og:image optimized for Facebook (with a large file size) will, more often than not, not display at all when someone shares it on Slack, for example.
Frequently, Yoast SEO has to share the same default og:image between multiple platforms – even though they have different expectations and apply different rules and treatments. Trying to work out what the ‘default’ image tag(s) should be, when it has to be the main image for Facebook and a universal default, is tricky. But it’s a problem we need to solve if we’re to provide a best practice ‘default’ setting for WordPress users.
There are lots of unknowns
Because each platform maintains its own rules and documentation on how they treat og:image tags, there are often gaps in our knowledge. Specific restrictions, edge cases, and in particular, information on which rules override other rules, are rarely well-documented. The documentation which does exist is often ambiguous at best. Google+ “snippet” documentation, for example, states that they won’t use images which are “not square enough”. It’s unclear what this means in specific, technical terms.
In order to determine the best universal approach to image sharing markup, we had to do some digging and some experimentation.
We were particularly interested in understanding how different platforms react to the presence of multiple og:image tags. If we can specify more than one image, and different platforms handle that differently, perhaps there’s a way in which we can get them to pick the most suitable image for their needs.
What we found
The way in which different platforms handle og:image tags (and in particular, multiple og:image tags) is often inconsistent, and frequently complex. Thankfully, most small platforms and apps simply crop and use the og:image tag (or the first og:image tag, if there are multiple in the set), and apply some reasonable constraints around dimensions, ratio, and file size. Some of the larger and more popular platforms, however, exhibit some particularly challenging behaviors, which complicate matters.
Here are some examples of the undocumented behaviors we’ve discovered (note that we’ve not talked about platforms which simply pick the first og:image tag, and which don’t exhibit any other ‘odd’ behavior). If you find any other undocumented features or behaviors which we haven’t covered or supported (or if we’ve made any mistakes in our research), we’d love to hear from you!
When multiple og:image tags are specified, Facebook uses the first tag in the set. This is in line with their documentation, but contrary to popular opinion (which assumes that the largest valid image is chosen). It’s also interesting to note that it uses the first image even if it’s invalid/broken.
Additional images are available for selection by the user at the point of sharing (on desktop only). Images are hard-cropped and sized to fit the sharing dialogue window, based on the size of the window.
Instagram behaves similarly to Facebook, except that it will only show an image preview for ‘small’ images (if the image file size is smaller than 300KB, and the dimensions are ~256×256 – though we’ve seen up to 400×400 work), and only supports JPG and PNG formats.
Twitter uses the last image in an og:image set, unless a twitter:image tag exists. Using the twitter:image tag allows us to control Twitter images independently of all other types, though unfortunately doesn’t allow us to specify multiple values (to accommodate for different tweet contexts/layouts).
To add some complexity, Twitter supports multiple card layouts, which can be defined in a twitter:card tag. The default value is ‘summary’ (1:1 ratio), but it’s also possible to specify ‘summary_large_image’ for a larger, full-width image (2:1) ratio.
Interestingly, Twitter used to support a ‘gallery’ type of card which held multiple images – however, they deprecated this into the ‘summary with large image’ card some time ago.
WhatsApp also uses the last image in the og:image set, which it hard crops to a small square. Note that, this appears to accept enormous images, both in terms of file size and dimensions; this red square is cropped from a 10000×10000, 1.49mb image.
Takes the first og:image, but caches it seemingly permanently (both locally and in the cloud), making it impossible to change/update an image thumbnail for a URL (without, e.g., manipulating the og:url value to include cache-busting elements).
Takes values from Facebook’s cache (typically the first og:image in a set). Cached images can be updated by messaging https://telegram.me/webpagebot (as a Telegram user) with up to 10 URLs. Note, caches only be updated if a page’s <html> tag contains a prefix=”og: http://ogp.me/ns#” attribute.
Pinterest’s documentation mentions that they “support” up to six og:image tags. However, sharing the page only ever utilizes the first* image the in the set.
They also support marking up inline images through Schema.org markup – however, when an og:image tag is present on the page (which will almost always be the case), it uses this instead.
There’s also some ambiguity around the difference between how they handle Open Graph data with ‘article pins’ vs ‘rich pins’. The latter is an ‘upgraded’ version which displays more information on the card, but using these requires the site owner to validate their domain.
*There’s an edge-case where, if there are more than six images in the array, the sharing dialogue periodically seems to choose the seventh value (and there’s some other weirdness depending on the total numbers of images in the array).
Google’s Web Fundamentals documentation implies that Google+ may prefer (and will prioritize) schema markup over Open Graph markup. Theoretically, that might allow us to enable allow users to set a specific image for Google+. They also do some of the smartest intelligent cropping and ratio management (or, at least, the documentation on their approach is more complete and transparent than others).
As an interesting aside, Google+ ignores robots.txt directives, and so may unwittingly expose private/hidden assets.
What we’ve considered, and our decisions
That’s a lot of moving parts. We need to compare all of these rules and decide which og:image tags we output for any given post or page, on any given WordPress site running the Yoast SEO plugin. That means optimizing for the most common and general use-cases, without breaking too many edge-cases. It also means providing tools, hooks, and filters in WordPress to allow users with special requirements to alter the behavior to meet their particular needs.
That’s why we’re choosing to optimize the first image in the og:set for large, high-resolution sharing – the kind which Facebook supports and requires, but which cause issues with networks which expect a smaller image (like Instagram, or Telegram) sharing.
Whilst you could argue that Facebook might not necessarily wield the influence and domination that it used to, it’s undoubtedly still a significant platform in terms of audience size, and a place where page/post sharing is prolific – and where the quality/treatment of the image is critical to click through.
Given that both Facebook, and most platforms’ default is the first og:image tag in a set, we must ensure that this image is a large, high-quality image (with a suitable aspect ratio for Facebook). Unfortunately, this approach has some side-effects. There’ll be many cases where the image used is too large for Instagram (and other platforms which expect small thumbnails) to feature in shared post links.
We’re not completely happy with this as a solution, but it’s the best compromise we can come up with. As an aside, we also believe that, in its current state, Open Graph markup is a bit broken. We think that it feels intuitively right that the first and default og:image in a set should be a high quality, high-resolution image – and that it’s the responsibility of the platform to crop this down appropriately, or to use secondary/smaller og:images, or to provide their own markup/solutions.
Ideally, Open Graph tags would inherit some of the kinds of thinking behind CSS media queries, where you can specify the different screen widths at which different sets of logic apply. We’ll be seeking to lobby and work with the various platforms to improve their support and collaboration in the coming months and years.
User context is an important factor
We also think that this compromise makes more sense than optimizing for smaller images, because the context in which smaller thumbnails are used is different.
We believe that individuals sharing URLs in one-to-one chat, or in small groups (e.g., in WhatsApp), are less likely to be negatively affected by a missing (or awkwardly sized/cropped) image. They’re usually chatting, engaged, and know the sender.
In the context of a newsfeed, like on Facebook or Twitter, the quality of the image is much more important – you’re scrolling through lots of noise, you’re less engaged, and a better image is an increased chance of a click/share/like.
In the case of Pinterest, and other systems where your interest is the image itself, we believe that most interaction occurs directly on the image, rather than from the page it’s on or a browser bookmarklet. Given this, we’re less concerned about Pinterest using the first og:image tag (which is a large image, optimized for Facebook) as a small, square thumbnail.
There’s an upper size limit, too
The biggest image you can have (both in terms of file size and dimensions) varies by platform, too. Some platforms support huge images (Facebook allows images to be up to 8mb) – but they chop these down into smaller thumbnails depending on the context. Some have relatively small max sizes, like LinkedIn and Telegram’s 2mb limit.
This makes it even more challenging to determine what the ‘best’ image should be, and which images should feature in the og:image set. We want to show a large, high-resolution image, but not too large.
It’s particularly tricky to pick the best size with WordPress, where we’re not always sure what image sizes we’ll be working with. That’s because, when a user uploads an image to WordPress, their site creates multiple versions of that image with different sizes and cropping. Typically, these are the original ‘full’ size, and ‘large’ (1024x1024px), ‘medium_large’ (768px, cropped), ‘medium’ (300x300px) and ‘thumbnail’ (150x150px) versions. But these default sizes are often altered by WordPress theme or plugin code, and by server configuration – and frequently, some might be too large for general use.
Because we need to make sure that the first og:image is suitable as a general default for as many platforms as possible, the Yoast SEO plugin evaluates post content, spots all of the images, and tries to pick the best size for each post. To get this right, we’ve evaluated the maximum file sizes and dimensions of a number of platforms, and we’ve set some automatic restrictions in the plugin.
- When the ‘full’ size image is over 2mb file size, and/or over 2000 pixels on either axis, and/or the ratio exceeds 3:1, we’ll try and fall back to a smaller standard WordPress image size.
- If we can’t find a suitable smaller image, we’ll continue to use the ‘full’ size. Note that this may result in the image not appearing in some sharing contexts.
We don’t want our users to have to micromanage the details of how all of this works. Of course, when you’re producing great content for your audience, you should consider how that content appears on third party and social platforms. But it should be as simple a matter of picking an appropriate image, and letting the system do the rest – from sizing and file management, to ensuring that the best version shows up when it’s shared in other locations. Because every platform follows its own rules, however, we’ve had to make some decisions which won’t please every user and won’t solve for every scenario.
For most normal use-cases, we’d suggest that you manually set og:image values on your posts via the Yoast SEO plugin, and ensure that their dimensions are between 1200x800px and 2000x1600px (and that they’re less than 2mb in size).
If you disagree with the decisions we’ve made, or want to help us improve our solution; we’d love for you to get in touch. WordPress and Yoast SEO are both open source products – you can help by explaining your use-cases, reporting your bugs, and thinking about how a better solution might work.
We’d love to hear your thoughts; the Open Graph is a mess at the moment, and it’s up to all of us to fix it.
Some additional technical details
We’ve taken some liberty in the og:image markup, and we’re aware that we’re adding quite a lot of weight and markup with this approach. Specifically, we’ll output HTML which looks something like this:
<meta property="og:image" content="https://www.example.com/main-image.jpg" /> <meta property="og:image:secure_url" content="https://www.example.com/main-image.jpg" /> <meta property="og:image:height" content="2000" /> <meta property="og:image:width" content="2000" /> <meta property="og:image:alt" content="A description of the image" /> <meta property="og:image:type" content="image/jpg" />
<meta property="og:image" content="https://www.example.com/second-image.jpg" /> <meta property="og:image:secure_url" content="https://www.example.com/second-image.jpg" /> <meta property="og:image:height" content="800" /> <meta property="og:image:width" content="800" /> <meta property="og:image:alt" content="A description of the image" /> <meta property="og:image:type" content="image/jpg" />
<meta property="og:image" content="https://www.example.com/third-image.jpg" /> <meta property="og:image:secure_url" content="https://www.example.com/third-image.jpg" /> <meta property="og:image:height" content="600" /> <meta property="og:image:width" content="400" /> <meta property="og:image:alt" content="A description of the image" /> <meta property="og:image:type" content="image/jpg" />
<meta property="og:image" content="https://www.example.com/fourth-image.jpg" /> <meta property="og:image:secure_url" content="https://www.example.com/fourth-image.jpg" /> <meta property="og:image:height" content="256" /> <meta property="og:image:width" content="256" /> <meta property="og:image:alt" content="A description of the image" /> <meta property="og:image:type" content="image/jpg" />
Note the progression ‘down’ from ‘large, high-quality image’, through different media sizes (depending on the site/theme setup), ending at a universal ‘small’ size.
We’ll also output a twitter:image tag, alongside the other twitter:card requirements (unless you’ve chosen to disable it in the Yoast SEO plugin config).
We’ll likely continue to iterate and improve on the approach, but here’s our rationale behind some of our assumptions:
- The og:image:type may not be strictly necessary in all cases, but there are many websites and server configurations where the images don’t have clean and recognizable ‘.jpg’ (or similar) file extensions. By making sure that we signpost the type of file, rather than making networks work it out, we reduce the risk of errors.
- Facebook’s documentation around how it uses secure_url tags is unclear, especially for sites which are fully HTTPS. However, in the case of video tags, it mentions explicitly that both tags are required, even if both feature the same HTTPS URL. As such, we’ll retain the secure_url tags even when your site and image are served over HTTPS.
- It’s generally considered best practice to label images with descriptive alt attributes, in order to support users who rely on screen readers and assistive technologies. We believe that Open Graph image tags shouldn’t be any different. This tag is only output when your images are labeled, so we’d encourage you to write some descriptive text during your image upload workflow.
- Our 2mb file size limit aligns, incidentally, to the default upload size set in most WordPress implementations which run on ‘off the shelf’ hosting.
- Our 2000×2000 pixel size flag should be a suitable maximum for almost all websites and screen sizes. Most browsers on desktop monitors have a width of fewer than 2,000 pixels (4k monitors and upwards often use image scaling to prevent everything from looking tiny). It’s also rare for any sharing ‘thumbnail’ activity (e.g., a Facebook message preview box) to take up the full width of the screen.
- As Google+ isn’t widely used, we’ve chosen not to add additional complexity to the Yoast SEO plugin by offering the ability to specify dedicated, schema-based image markup for Google+ images. In most cases, we believe that the default og:image should be a suitable option for Google+ sharing – though we’re keen to hear from you if you find that this is not the case.
- Unlike most other networks, WhatsApp supports SVG file formats. That means that, in theory, you could achieve optimal sharing for both WhatsApp and Facebook by setting your first og:image to be an SVG, and setting your second og:image as your full-res, large image. However. many other networks only read the first image, and won’t use the SVG file. SVG formats also come with a myriad of security risks, and so we’re not comfortable recommending their general use in this context.
Some undocumented Facebook ‘features’
If you’re feeling particularly geeky, you might also enjoy the following discoveries!
- In addition to specifying the URL of an image, you can specify its height and width. This has some benefits, including encouraging Facebook to pre-cache the image on the first time it’s shared. However, when you specify multiple og:image tags, invalid/malformed height/width in any of those tags may cause problems with all of your images. E.g., an invalid og:image:height or og:image:width value on an image which isn’t chosen, prevents pre-rendering.
- Specifying an image triggers the pre-caching process, regardless of whether it’s correct or not.
- Facebook has different ‘share layout’ sizes, depending on the image size. Small images don’t scale up to a large preview very well, so they provide a condensed layout. However, the share layout size sometimes defaults to accommodating the smallest image from a set (e.g., if you have 10 huge og:image tags, and 1 small one, you sometimes get the small share layout).
- Facebook also sometimes falls back to the ‘small’ layout if you have too many broken images in your set (as Facebook’s broken image file is only 540×300).
- Setting incorrect image sizes lets you upscale small images in the Facebook debugger, but it doesn’t look like this is respected in the share dialogue. You cannot ‘downscale’ images so far as I can tell. There’s a “upscale=1” parameter in the version of the image which Facebook creates, which I suspect controls this.
- Large images break! The maximum square image size appears to be in the region of 9200×9200. However, some images with unequal dimensions larger than this, but a lower total area (e.g., 10000×3000) work, as long as a 3:1 ratio or higher.This suggests that the boundaries might be based, in part, on a maximum square area of ~95,000,000 pixels.
- As a minor additional note, when sharing a too-large image directly (i.e., linking directly to the file itself), Facebook just shows a blank image – there’s no fallback file/function used in their ‘safe image’ cleaner in this context.
- Facebook supports a really interesting feature which lets you build relationships between pages featuring partial/linked Open Graph information. This, theoretically, allows you to ‘inherit’ and/or place centralized og markup elsewhere, reducing page weight. This might be useful for mobile/responsive subdomains, and some internationalization/versioning scenarios, perhaps, if other platforms supported it.
- Additionally, where cloaking is often a risky tactic in SEO (and frowned upon by Google), Facebook actively suggest (see “You can target one of these user agents to serve the crawler a nonpublic version of your page”) cloaking mechanisms as a method of managing scenarios with paywalls, struggling servers, and other scenarios.
- Despite claiming otherwise on their documentation, like Google+, Facebook’s crawler appears to ignore or disregard robots.txt directives – it’ll happily fetch Open Graph values from pages which are blocked by robots.txt files.
Some potential “hacks”?
Being stuck having to ‘share’ the first og:image tag between multiple networks is, as we’re discovering, limiting, and not ideal. Sticking to the Open Graph standards, as they’re written, lumps us with all sorts of unfortunate compromises and dead-ends.
So what if we think outside the rules a bit?
If we’re creative, there may be some clever tricks or undocumented approaches which we can use to bypass, confuse, or force certain behaviors from some of the trickier platforms.
Here are a few approaches we’ve considered, but ultimately discarded.
Can we use platform detection to create a dynamic solution?
Imagine for a moment, that every time a page is shared, the platform visits that page, reads the og:image tag(s), and grabs the image.
Theoretically, the Yoast SEO plugin could detect which platform is requesting the page or image, and execute some clever code to serve it the perfect image for its requirements.
That’d enable us to ensure that, regardless of what’s being shared, and where, we could intelligently make sure that the first og:image is the best option for the scenario.
However, the platforms don’t visit every time – they visit once, and save a copy of the tags (and the images) they found for a little while. That length of time, and the scenarios which cause them to revisit and/or update their cached version vary wildly by platform.
Still, theoretically, the Yoast SEO plugin could try and serve the right tags to the platform when they do visit, at the moment when they create their cache. But this approach relies heavily on the platforms identifying themselves when they visit (and on us recognizing their identities), on the website in question having a certain type of server configuration, and on our software doing some tricksy logic around deciding which tag(s) to show.
It might also open things up to forms of abuse by users and platforms who falsify their identities, and it won’t work for any website running any form of advanced caching (where the static pages are served to most visitors).
It all gets pretty complicated, and it’s not a robust enough approach to rely on.
Hidden image techniques
Some platforms, like Pinterest, do more than just grab the og:image tag(s) – they scan the page and look for other images too. That means that we can place sharing-optimized images outside of the Open Graph tags, as part of the page content, and hope that users select these when sharing.
In most scenarios, those images don’t necessarily need to be visible, in order to be discovered. We can place hidden images in the source code of a page.
Unfortunately, this technique doesn’t help in most cases, other than increasing the chance of an image showing up in a selection process. e.g., where Facebook allows you to select a thumbnail from an og:image set, or where Pinterest allows you to choose an image to share from a page when using their browser bookmarklet).
In most scenarios, in-page images are ignored when og:image tags are specified. Hiding images can also cause unexpected side-effects, such as accessibility issues, or slower page loads – especially when sharing the kinds of large images which platforms like Pinterest or Facebook look for in cases like this.
For some platforms, like Facebook, we can ‘push’ a specific image to them, by setting the first og:image and sending a request to their cache-busting URL.
We could adapt the Yoast SEO plugin to set a specific image as the first in the set, then ping the relevant platform’s cache-busting system to update the image. Then we could change the og:image ordering and repeat the process, setting the best image for each platform which allows for remote cache updating
Unfortunately, this only allows us to set the initial image. When their caches expire, we’re back to square one – they’ll use the logic we’ve outlined to pick their preferred images.
To repeat the cache-setting process, we’d need to constantly juggle the order of the tags, in line with the specific cache-expiry times of each platform. This adds a wealth of complexity, and simply isn’t feasible in most site setups (especially those running their own caching solutions), and only a few platforms support this kind of cache-busting.
More importantly, we want to avoid tactical hacks
As we’ve explored, we’re not keen on going to these kinds of lengths to try and fix a problem which could be fixed so much more effectively, and comprehensively, by the platforms themselves, and improvements to the Open Graph protocol.
We’re not adverse to implementing clever, technical solutions to help get the image selection process right, but we’d rather work with the platforms to address the problems at the source, rather than tackling the symptoms across the 8 million+ websites running the Yoast SEO plugin.
We’d love to hear your thoughts on how you think WordPress, Yoast SEO, the Open Graph Protocol and big platforms like Facebook and Twitter might be able to work together better!