Debugging Slack Link Unfurling

By Dave Allie
Published Aug 4, 20244 min read
A few years ago, after seeing the cover images on several other blogs. I felt inspired to do the same. Including a cover image for content when it’s linked externally just adds a little pizazz, drawing a little more attention and making it look a little more polished.

Unfurling

If you haven’t heard of it before, link unfurling is the process of parsing the details of a posted link and extracting the relevant information to enrich the preview of that link. That includes things like website names, page titles, descriptions, authors, and (importantly for me) cover images.
When unfurling links, the fallback approach most social platforms take is to extract title, cover, and description from the content directly. So for a very simple HTML page with an image:
<html>
<head>
  <title>My Blog Post</title>
</head>
<body>
  My blog content! Here, have a picture of a puppy:
  <img src="https://picsum.photos/id/237/800/600" />
</body>
</html>
You may get a link preview like this:
simple dog
There are a couple of problems with this:
  • Not all platforms will pull out the picture of the dog from the content, they may just show the title
  • If you have multiple images in your body, unfurlers will just take the first one, even if it’s not the one you want to show in the preview
Thankfully we can provide some guidance to unfurlers, through the use of <meta> HTML tags.

Open Graph meta tags

Instead of relying on each social platform to correctly and consistently unfurl our link based on the body of the content, we can use the Open Graph protocol to declare the page title, description, author, and cover image which will be used for showing a preview of the page.
Slack’s API docs do a great job of illustrating how unfurling works with Open Graph and which specific meta tags are used to populate each piece of data.
Source: Slack
I put together a simple image template, exported an image for each of my past posts, and made sure I had the relevant <meta> tag in the head for each post. Here’s a look at the cover image for the post you’re reading right now:
I shipped it, called it a day and moved on. Only, when I came back to test it a little later in the day, I realised that it wasn’t working at all on Slack. In fact, none of the Open Graph meta tags were working, my links had no preview at all.
What I wanted:
What I got:

Why Slack, why?

I started by inspecting my compiled HTML pages and confirmed that I did indeed have all the necessary meta tags. I previewed my blog post links on LinkedIn, Twitter, Facebook and saw that they were all working. Even when previewing my page on https://metatags.io/, my blog links looked correct for all platforms, including Slack!
Completely confused by what in the world was going on, I started a simple Python HTTP server locally and exposed it through ngrok. I pasted the new public link into Slack, and inspected the request in the ngrok web portal.
Turns out, Slack includes the Range HTTP header when unfurling links which asks the server to respond with a particular byte range of the full response, in this case Slack is asking for the first 32kB of the response.
I plugged my ngrok link into several other social platforms where my unfurling was working, and comparing Slack with them highlighted a pretty obvious difference. Slack was by far the most restrictive with its content fetching, limiting itself to a measly 32kB, followed by Facebook at 512kB, Twitter at 1MB, and then LinkedIn at a whopping 3MB.
info_outline
Twitter doesn’t actually include a Range header, but as far as I can tell, it only parses the first 1MB of content from the response. If you’re a Twitter engineer and reading this, save some bytes and throw a header on those requests.
Inspecting the raw HTML from my blog post, and the position of the Open Graph meta tags finally made it clear why Slack unfurling wasn’t working while other platforms were. I’ve represented the length HTML response in red, and then the position of the meta tags as the blue line.
My blog, which is hosted on Vercel, was being a good little compliant HTTP server and responding with the first 32kB of the HTML page, which didn't contain the meta tags. It turns out that Gatsby was inserting a lot of CSS into the head of my page (to ensure that content looks correct on first load) and then inserting the meta tags below that, pushing the meta tags outside of that 32kB window.

Fixing the problem

Adding a custom html.js file to my Gatsby setup allowed me to ensure that <meta> and <title> tags in the HTML <head> are always positioned before other tags (code).
With this change, the position of the Open Graph meta tags are well within the first 32kB of the HTML response.
And with that small change to positioning, my links were working in Slack!