Using htaccess to provide better Open Graph images

I joined David Sparks and Rosemary Orchard on episode 20 of the Automators podcast. It was a riot, and made me realize exactly how nerdy I am about automation and its peripheral nerdery. One of the things that came up was my htaccess trick for handling Open Graph metadata on my blog. I got a bunch of questions about that, so I’m writing this up to explain.

If you’re unfamiliar with Open Graph, it’s a protocol which allows you to use meta tags in HTML to explain to various services what a page/post is, what image should represent it, and other information about a page. When you share a URL on Twitter or Facebook and it shows an image, summary, author information, etc., Open Graph is what allows the creator to control what’s shown.

Facebook and Twitter each have their own specs for preferred image dimensions and minimum size. I won’t go into all of those details right now, but if you’re setting up a system for yourself you’ll want to search the web for the latest information (it changes now and then). Different Open Graph tags target specific services, so I need different images for each service.

Here are my current sizes generated:

  • _tw (Twitter): 715x383
  • _fb (Facebook): 476x249
  • _sq (Square, Twitter small size): 158x158

My setup automatically generates all of the necessary sizes from a template I use, naming each one with a suffix for the particular service it’s for. Sometimes, though, I don’t have a certain size available, which is where the .htaccess trick will come in.

In my templates I generate a standard boilerplate based on the name of the primary image for the post, appending the suffixes expected:

<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="[url/path]/image-filename_tw.jpg">
<meta property="og:image" content="[url/path]/image-filename_fb.jpg">
<meta property="og:image:type" content="image/jpeg" />
<meta property="og:image:width" content="476">
<meta property="og:image:height" content="249">

Twitter doesn’t have specific tags for width and height, so those only apply to the Facebook image. My dimension tags are created using a call to sips when the static site is generated.

So what happens when a file with the appropriate suffix doesn’t exist? The .htaccess has a cascade of fallbacks, so when the image specified is requested but doesn’t exist, something gets served back. It checks to see if each requested filename exists, then rewrites the filename as the next option, repeating until finally it just falls back to serving the original image from my post.

For example, my current Web Excursions header doesn’t have a _tw version. I actually added that size since I created that image. So while the HTML specifies https://cdn3.brettterpstra.com/uploads/2017/03/web-exc-map_tw.jpg as the Twitter image, when you put that in a browser, you’ll actually be served the Facebook version: https://cdn3.brettterpstra.com/uploads/2017/03/web-exc-map_fb.jpg. Which is close enough in this case, each service will crop as needed. Having the right dimensions to begin with simply gives you control over how the image appears.

The Twitter Card for my last Web Excursions post
The Twitter Card for my last Web Excursions post

Here are the rules in my .htaccess file:

# Image handling for open graph meta

# Try _fb if _tw doesn't exist
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)_tw\.(jpg|png|gif) $1_fb.$2 [L]

# Try _sq if _fb doesn't exist
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)_fb\.(jpg|png|gif) $1_sq.$2 [L]

# Try _lg if _sq doesn't exist
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)_sq\.(jpg|png|gif) $1_lg.$2 [L]

# Try @2x if _lg doesn't exist
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)_lg\.(jpg|png|gif) $1@2x.$2 [L]

# Fall back to base image file if no @2x
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)@2x\.(jpg|png|gif) $1.$2 [L]

By the way, this has the side effect of letting xxx@2x.jpg fall back to xxx.jpg if the @2x doesn’t exist, so I can always request @2x when serving to retina displays, even if I haven’t created one.

In summary, this system allows me to easily provide different images for different services, but gracefully handles cases where I don’t generate all of the needed sizes without me needing to modify meta or templates each time. The htaccess part of the trick is really just the tail end, but enough people asked specifically about it that it seems worth sharing. Hope that helps, feel free to contact me with any specific questions!

Brett Terpstra

Brett is a writer and developer living in Minnesota, USA. You can follow him as ttscoff on Twitter, GitHub, and Mastodon. Keep up with this blog by subscribing in your favorite news reader.

This content is supported by readers like you.