The LinkedIn Sharing Paradox: Why Testing Social Media Integration is Harder Than It Should Be
Summary
Today I learned you test LinkedIn sharing in production, because there's nowhere else to test it. LinkedIn's crawler scrapes your Open Graph tags once and caches them, so fixing a typo afterward just leaves a stale preview and never tells you it did. Clearing your own browser cache does nothing, because the cache lives on their servers, not yours. You can't test it locally either, since the crawler never reaches localhost. What did eventually get me unstuck was LinkedIn's Post Inspector, a ?v=2 cache-bust on the URL, and checking that my Django Site domain was actually right.
Today I learned that you test LinkedIn sharing in production, because there is nowhere else to test it.
The mechanism is simple. Share a link and LinkedIn's crawler visits your URL right away, scraping your Open Graph meta tags:
<meta property="og:title" content="Your Article Title">
<meta property="og:description" content="A compelling description">
<meta property="og:image" content="https://yoursite.com/image.jpg">
<meta property="og:url" content="https://yoursite.com/article">
Four tags. Writing them was the fast part, and then the whole evening disappeared into finding out whether they work.
LinkedIn Never Forgets
LinkedIn scrapes your page once and caches what it finds. Then it moves on. Fix a typo in the title afterward? LinkedIn already has its copy and won't be back for a while. No error, no warning. Just a stale preview staring back at you while you clear your browser cache for the third time, which does nothing, because the cache lives on their servers.
You can't get ahead of it locally either, since the crawler can't reach localhost:8000. Every option is bad in its own way. You can deploy to production and test live, which is risky. You can tunnel out with ngrok, which is one more moving part to babysit. You can spin up a staging environment, assuming you have one. Or you can ship it and hope, which, let's be honest, we've all done.
The Sites Framework Would Like a Word
Django's Sites framework stores your domain in the database, which is its own little trap:
# This needs to match your actual domain
Site.objects.get_current().domain # 'example.com' vs 'yoursite.com'
If that row is wrong, every absolute URL your site generates is wrong, and your og:url points somewhere that isn't your article. And your local database and your production database each carry their own Site entry, so a preview that behaves in development proves nothing about production.
Escape Hatches
LinkedIn's Post Inspector forces a re-scrape of your URL. It works, mostly, and it sits buried in the developer documentation where nobody casually browsing will trip over it. Bookmark it now.
The cache is also keyed on the exact URL, so /article?v=2 counts as a brand new page. Bump the number, get a fresh scrape. I'm not proud of it.
And check the Site domain before you reach for either trick:
python manage.py shell
from django.contrib.sites.models import Site
Site.objects.get_current().domain # Should be your actual domain
The honest answer, though, is a staging environment with a real domain, because you can't truly test this on localhost no matter how clever you get.
It bugs me that deploying a containerized microservice takes less ceremony than previewing how one link renders on LinkedIn. But that same caching, the thing that ate my evening, is also what lets LinkedIn serve billions of link previews without melting a data center. They built it for their scale, and one developer's debugging session doesn't register.
So here's the procedure, such as it is. If the preview is wrong, it's probably cached, so run the Post Inspector or bump ?v=. If it's still wrong after that, the problem is almost always the Site domain in the database, so check that next.