[KongchangAI]
· 2 min read· 1,029 words

A Cache Incident in an OG Image Generator: The Culprit Was the URL

A Cache Incident in an OG Image Generator: The Culprit Was the URL

An OG image generator returned blank previews for 40% of shares because the CDN used the URL as cache key and Slack's crawler stripped the query string.

After a developer's OG image generation API was hooked into a Slack link-unfurling pipeline, about 40% of previews turned into gray blanks. The root cause wasn't the renderer but CDN caching: image URLs carried the title and description in the query string, but Slack's unfurling bot strips the query string in some clients, collapsing requests for different content into one cache key so the first response was cached for all later ones. The fix is to hash the full parameter set into the URL path and make the query string optional, keeping cache keys and content consistent. The article notes that the gap between test and real request environments is the main blind spot and advises developers to test with different platform crawlers and check what the CDN actually sees rather than just the URL they send.

A Cache Incident in an OG Image Generator: The Culprit Was the URL

When 40% of Slack Previews Turned Into Gray Blocks

A developer built an OG image generation API that takes a title and description and renders a 1200x630 card. Everything worked fine in local testing. But when he hooked it into a link-unfurling pipeline that had an agent post dozens of links to a Slack channel, suddenly a third of the preview images turned into gray blanks.

The problem wasn't the renderer—it was the CDN sitting in front of it. This is a real experience the developer shared on his blog, and the original title gets straight to the point: "My OG Image Generator Returns Blank PNGs for 40% of Slack Previews—The Cache Key Is the URL, Not the Title."

Illustration of the OG image generator cache problem

The Root Cause: Cache Keys Out of Sync With Content

Each generated image URL looked like /og?title=...&desc=.... The CDN cached based on the full URL, which in itself is correct. But here's the problem: Slack's link-unfurling bot strips the query string before requesting the image in some clients. So ?title=Deploy+failed and ?title=Deploy+succeeded both collapsed into /og/, and the first response got cached for all subsequent requests. As a result, a "Deploy failed" card showed up in a channel full of "Deploy succeeded" links.

This bug reveals a fragile point in generative image APIs: when URL parameters carry the content, the habit of "the URL is the cache key" needs to be audited. According to the developer, the fix was simple and effective—hash the full parameter set into the path itself (e.g., /og/a3f9c2….png) and make the query string optional. The cache key and the content are now consistent.

An OG image (Open Graph Image) is part of the Open Graph protocol. Proposed by Facebook in 2010, the protocol embeds structured metadata in HTML tags such as <meta property="og:image">, allowing social platforms to automatically fetch the title, description, and thumbnail when a link is shared and generate a rich-media preview card. OG images are typically designed at 1200×630 pixels to fit the hero image aspect ratio used by most platforms. When a link is pasted into Slack, Twitter, Facebook, or similar platforms, the platform's crawler visits the URL, parses the OG tags, and pulls the image for the preview. If the image URL returns a blank or incorrect response, the preview shows up as a gray block or placeholder. Understanding how the OG protocol works helps you pinpoint whether the issue lies in missing metadata, a failed image render, or a cache layer returning the wrong response.

Link unfurling is a feature of collaboration platforms like Slack: when a user pastes a URL, the platform's backend asynchronously fetches the link's content and generates a preview card with a title, description, and thumbnail. This process is handled by the platform's crawler or "unfurler bot," not the user's browser. The key point is that the unfurler implementation can differ across clients (desktop, mobile, web), including how it normalizes URLs—for example, whether it preserves the query string, decodes special characters, or ignores fragment identifiers. This means the URL a developer constructs may already be rewritten by the platform before it reaches the image service. The request the cache system sees and the request the developer sent can therefore diverge—which is exactly what caused the query string to be stripped and the cache key to collapse in this case.

Why Is This Problem So Easy to Miss?

The gap between the test environment and the real request environment is the main blind spot. When the developer tested locally, requests carried the full query string and the CDN behaved as expected. But the behavior of Slack's unfurling bot in "some clients" wasn't simulated. This isn't an isolated case: many link-preview services and social platform crawlers have their own URL-shuffling logic—some strip query parameters, some ignore fragment identifiers, some are case-sensitive about paths.

On the other hand, the CDN's cache layer is usually "transparent" to developers—whatever you send is what it caches. But the CDN sees the request after it's been processed by the client (or middleware), not the URL you originally constructed. This difference in perspective is a breeding ground for cache incidents.

The caching mechanism of a CDN (Content Delivery Network) follows a key-value model: the cache key is usually the full request URL (including the query string), and the value is the response content. When a subsequent request's cache key matches an existing entry, the CDN returns the cached value directly without going back to the origin. This design is efficient when URL parameters don't affect the content, but when the URL parameters themselves identify the content, any rewriting of the URL causes the cache key and content to fall out of sync. More insidiously, the CDN's cache layer is usually opaque to developers—developers only see the requests they send, while the CDN sees requests after they've passed through clients, middleware, and proxies. This difference in perspective means cache incidents often only surface under real traffic and are hard to reproduce in local testing.

Actionable Advice for Builders

If you're building link previews, unfurlers, or image-API-based card generators, this developer's lessons are worth noting:

  • Check what the CDN sees, not just what you send. Test previews with different clients (especially the crawlers of platforms like Slack, Twitter, and Facebook).
  • Embed a content hash in the path rather than relying entirely on the query string. The path is a more stable cache identifier.
  • Make the query string optional, ensuring that even if it's stripped, the correct (or at least distinguishable) content is returned.
  • Monitor preview failure rates. If you notice an abnormally high share of blank previews for a certain type of link, check the cache layer first.

The developer has packaged the tool at x402.freeq.one for anyone interested in studying it. As he put it: "The blank-card bug taught me more about HTTP caching than any blog post ever could." This case once again proves that cache correctness in distributed systems often hinges on the most inconspicuous details—like whether a query string survives or not.

#OG image#CDN caching#Slack link preview#cache key#Open Graph protocol
Share:

Related articles