An embedded post is a pipe to the platform
Embed an Instagram post, an X (Twitter) tweet, a TikTok video, or a Facebook post, and the content is only half of what happens. You are also loading the platform's widget script (platform.instagram.com/en_US/embeds.js, platform.twitter.com/widgets.js, TikTok's embed.js, connect.facebook.net) which connects to the platform and can set cookies and fingerprint the visitor the instant the page loads. The visitor did not click the post. They did not interact with the platform. Their browser told Meta, X, or TikTok that they were on your page anyway.
That default behavior is the problem, and a European court decided years ago that you are on the hook for it.
Fashion ID: why the embed is your responsibility
On 29 July 2019, the Court of Justice of the EU ruled in the Fashion ID case (C-40/17) that a website operator embedding Facebook's "Like" button is a joint controller with Facebook for the collection and transmission of visitors' personal data. The court's reasoning was blunt: by placing the plugin, Fashion ID made it possible for Facebook to obtain visitors' data, and it happened whether or not the visitor was a Facebook member and whether or not they clicked. Because the operator helped determine that this collection and transmission took place, the operator shares responsibility for it and must inform users and have a legal basis before the data flows.
The ruling was about a Like button, but the logic covers any embedded social plugin that transmits visitor data on load: post embeds, follow buttons, share widgets, timelines. If it phones the platform before the visitor acts, you are a joint controller for that transmission, and consent is the legal basis that fits.
The old German answer: two-click, and Shariff
German publishers hit this problem long before Fashion ID and built the fix that everyone now uses. The computer magazine c't (heise) popularized the "two-click" solution and released an open-source library called Shariff. The idea is simple: social buttons and embeds transmit nothing to the platform until the visitor actively clicks to engage. The first click loads the platform content, with the visitor now informed; only then does any connection to Facebook, X, or the rest occur. No personal data leaves the page on a passive visit.
That pattern is now called click-to-load, and it is the standard way to embed social content compliantly. You render a placeholder, you tell the visitor what loading it involves, and you load the real embed only on their click.
Implementing click-to-load
The facade is a small box that describes the content and sets nothing. On click, you inject the platform's real embed markup and script:
<div class="social-embed"
data-platform="instagram"
data-url="https://www.instagram.com/p/POST_ID/">
<p>This post is hosted on Instagram. Loading it connects to
Meta and may set cookies.</p>
<button type="button" class="social-embed__load">
Load Instagram post
</button>
</div>
document.querySelectorAll('.social-embed__load').forEach((btn) => {
btn.addEventListener('click', (e) => {
const box = e.target.closest('.social-embed');
const quote = document.createElement('blockquote');
quote.className = 'instagram-media';
quote.setAttribute('data-instgrm-permalink', box.dataset.url);
box.replaceChildren(quote);
const s = document.createElement('script');
s.async = true;
s.src = 'https://www.instagram.com/embed.js';
document.body.appendChild(s);
});
});
The same structure works for X (inject the tweet markup, then platform.twitter.com/widgets.js), TikTok, and Facebook: hold the widget script out of the initial page, and add it only when the visitor clicks. If you would rather drive it from your consent banner, treat each platform's widget script as a marketing tag and let your consent platform release it once the marketing category is granted, the same iframe-and-script gating used for video and maps.
A stronger option: fetch the embed server-side
Click-to-load is the standard fix, but there is a more thorough one for content you control. Most platforms expose an oEmbed endpoint that returns a post's HTML, and some let you render a static version. If you fetch and cache that markup on your server, or capture a screenshot of the post when you publish, you can display the content as a first-party asset with no client-side connection to the platform at all, and nothing to gate.
The trade-off is that live features (a working play button, updated like counts) may not survive, and some platforms restrict caching in their terms, so check each one's rules. For a feed you rarely change, a cached or screenshot embed removes the third-party leak completely. For a live, interactive feed, click-to-load is the pragmatic default.
Embeds are not the same as pixels
Keep two things separate. A social embed displays content and leaks data through the widget script. A tracking pixel (the Meta Pixel, the TikTok Pixel) is a dedicated measurement tag whose whole job is to send events back to the platform for ad targeting. Both need consent, but they are different integrations with different fixes. Click-to-load handles embeds; pixels are gated at the tag level and increasingly need server-side handling, covered in our guides on Meta CAPI consent and the TikTok Events API. A site can have both problems at once: the Instagram feed in the footer and the Meta Pixel in the head are two separate leaks.
Getting it right
- No social widget script loads on page render. A facade stands in until the visitor clicks or grants consent.
- The placeholder tells the visitor the embed connects to the platform and may set cookies, so their click is informed.
- Verify in the network panel that nothing hits
connect.facebook.net,platform.twitter.com,platform.instagram.com, or TikTok before that action. - Keep your privacy notice honest about which platforms you embed and that you are a joint controller for the transmission.
- Treat embedded content and tracking pixels as separate jobs.
A consent platform like CookieBeam blocks these widget scripts until the matching category is granted and reports the outbound connections each embed opens, so a stray Instagram block on one landing page shows up in the scan instead of quietly leaking. Fashion ID made the responsibility clear; click-to-load is how you meet it without dropping social content from your site.