WordPress External Featured Image via cURL

on in Blog
Last modified on

WordPress External Featured Image

Here’s the long title for this code snippet —

How to set up an external image as a WordPress featured image and inject it into header as an og:image meta tag, suitable for LinkedIn and Facebook

Why did I do this? Because Yoast SEO does not add an og:image tag automatically, if there is no locally hosted featured image. I also did it because in this very specific case, my images were served from a script and generated on the fly based on width, height, quality and cropping URL parameters. Here’s my workaround:

/**
 * Use external URL for featured image (helps with social sharing)
 * 
 * Get the cURL effective URL and save it in a custom meta field.
 * Output custom Open Graph meta tags based on the saved URL.
 */
function whiskey_thumbnail_external_replace() {
    global $post;

    if (empty($post->ID) || (string) get_post_type($post->ID) !== 'custom-post-type') {
        return;
    } else {
        if ((string) get_post_meta($post->ID, '_whiskey_featured_image', true) === '') {
            $url = 'path/to/image/;

            // Use cURL to get the image path if it doesn't exist
            $curl = curl_init();
            curl_setopt_array($curl, [
                CURLOPT_URL => $url,
                CURLOPT_HEADER => true,
                CURLOPT_NOBODY => true,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_MAXREDIRS => 10,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_CUSTOMREQUEST => 'GET',
                CURLOPT_ENCODING => ''
            ]);
            $response = curl_exec($curl);
            $redir = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
            curl_close($curl);
            //

            $url = $redir;

            update_post_meta($post->ID, '_whiskey_featured_image', $url);
        } else {
            $url = get_post_meta($post->ID, '_whiskey_featured_image', true);
        }

        if ((string) $url !== '') {
            add_filter('wpseo_frontend_presenter_classes', 'whiskey_wpseo_frontend_presenters', 10, 1);

            // LinkedIn
            echo '<meta name="image" property="og:image" content="' . $url . '">';

            // Facebook
            echo '<meta property="og:image" content="' . $url . '">';
            echo '<meta property="og:image:alt" content="' . get_the_title($post->ID) . '">';

            if (getimagesize($url)) {
                $size = getimagesize($url);

                echo '<meta property="og:image:width" content="' . $size[0] . '">
                <meta property="og:image:height" content="' . $size[1] . '">';
            }
        }
    }
}

function whiskey_wpseo_frontend_presenters($presenters) {
    if ($matches = preg_grep('/Image_Presenter/', $presenters)) {
        return array_diff($presenters, $matches);
    } else {
        return $presenters;
    }
}

add_action('wp_head', 'whiskey_thumbnail_external_replace', -1000);

Note that I am storing the final URL in a custom meta field and I am using CURLINFO_EFFECTIVE_URL to get the last effective URL (after all redirects). Also note that you need CURLOPT_FOLLOWLOCATION to be set to true.

After I get the image, I am adding 5 meta tags (one for LinkedIn and 4 for Facebook). Here they are:

<!-- LinkedIn -->
<meta name="image" property="og:image" content="/path/to/image/">

<!-- Facebook -->
<meta property="og:image" content="/path/to/image/">
<meta property="og:image:alt" content="This is the title">
<meta property="og:image:width" content="1920">
<meta property="og:image:height" content="1080">

If you are using an SEO plugin other than Yoast’s, you might not need this workaround.

Photo by Eric Park on Unsplash.

Related posts