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 with no error and no warning, and clearing your own browser cache does nothing because the cache lives on their servers. Debugging it feels like working through a one-way mirror while wearing oven mitts. You can't even test it locally. The crawler never reaches localhost. The workarounds are LinkedIn's Post Inspector, a ?v=2 cache-bust on the URL, and double-checking that your Django Site domain is 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. Your options are all bad: deploy to production and test live (risky), tunnel out with ngrok (one more moving part), use a staging environment (if you have one), or ship it and hope (we've all been there).
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 the same caching that ate my evening is what lets them serve billions of link previews without melting a data center. LinkedIn built it for LinkedIn, and my debugging session is a rounding error to them.
So here's the procedure, such as it is. A wrong preview means it's cached, so run the Post Inspector or bump ?v=. Wrong after that means the Site domain. Go check the Site domain.