/ โ† Social Preview Fixes / Discord Preview
In this guide
  1. What each part of a Discord embed comes from
  2. How Discordbot fetches your page
  3. A head section Discord reads cleanly
  4. oEmbed: author and provider lines
  5. Server, channel and user settings that hide embeds
  6. Discord embed caching and how to get a fresh card
  7. Bots and webhooks: custom embeds and switching them off
  8. The fix, step by step
  9. Platform notes and keeping embeds healthy
  10. Questions and answers

What each part of a Discord embed comes from

Every visible piece of a Discord embed is filled from a particular tag or file. Once you know which one feeds which part, you can look at a broken card and go straight to the tag responsible, rather than guessing.

Part of the embedWhere Discord gets itIf it is wrong or missing
Coloured strip on the left<meta name="theme-color">The strip falls back to Discord's default neutral shade.
Provider line (small text at the top)og:site_name, or provider_name from an oEmbed fileNo provider line appears.
Author lineauthor_name and author_url from an oEmbed fileNo author line; ordinary pages rarely have one.
Headlineog:title, then twitter:title, then the <title> elementA generic site-wide title, or the embed is skipped.
Snippet under the headlineog:description, then twitter:description or the meta descriptionHeadline only, or boilerplate from your theme.
Pictureog:image (or twitter:image), sized by twitter:cardNo picture, or a small square thumbnail on the right.
Playable videoog:video with its type, width and heightA still image instead of a player.

The layout switch trips up a lot of people. With twitter:card set to summary_large_image, the picture sits full width below the snippet. Without it, or with summary, Discord tends to shrink the picture into a thumbnail tucked beside the text, which is fine for a logo but wastes a designed share graphic.

๐Ÿ’ก Discord does not offer a public embed debugger. Your best diagnostic is a small private server with one text channel that only you can see, used purely for pasting test links.

How Discordbot fetches your page

As soon as a message with a link is sent, Discord's back end requests the address from its own servers. The request carries a user agent that includes Discordbot; at the time of writing it reads along the lines of Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com). That single request decides what the card shows, and a few consequences follow.

Proving the request arrived

Paste the link into your test channel, then look at the access log within a minute. This one-liner prints the status code and path of each recent Discordbot hit on a typical nginx or Apache combined log:

grep -i "discordbot" /var/log/nginx/access.log | tail -n 10 | awk '{print $9, $7}'

Read the result like this: nothing at all means something upstream (a CDN, a WAF or a hosting firewall) stopped the bot before your server saw it; 403, 429 or 503 means your stack refused it; 301 or 302 shows a redirect hop worth removing; 200 on the page but no line for the image means the image URL is the weak point.

Letting it through a firewall

On Cloudflare, a custom rule with the action Skip and an expression such as (http.user_agent contains "Discordbot") lets the crawler bypass managed challenges and Bot Fight Mode settings that support skipping; check your plan's options, because not every bot feature can be skipped on every tier. In Wordfence, ModSecurity or a host's "bad bot" list, whitelist the same user-agent string. Keep the rule narrow: allow the user agent on public pages and your share-image folder, not on admin paths.

โš ๏ธ User-agent strings can be faked. An allow rule for Discordbot should only skip challenges on public content; it should never unlock anything a normal anonymous visitor could not already see.

A head section Discord reads cleanly

Here is a complete, working head for a fictional community event page. It gives Discord everything it uses: a headline, snippet, provider line, large picture, strip colour and an oEmbed file for an author line.

<head>
  <meta charset="UTF-8">
  <title>Winter LAN weekend, 14-15 February | Northgate Gaming Society</title>
  <meta name="description" content="Two days of casual and ranked play, a beginners' bracket on Saturday morning and hot food all weekend.">
  <link rel="canonical" href="https://example.co.uk/events/winter-lan">
  <meta name="theme-color" content="#2e8b57">
  <meta property="og:type" content="website">
  <meta property="og:site_name" content="Northgate Gaming Society">
  <meta property="og:url" content="https://example.co.uk/events/winter-lan">
  <meta property="og:title" content="Winter LAN weekend: 40 seats, book by 7 February">
  <meta property="og:description" content="Bring your own rig or borrow one of ours. Beginners' bracket Saturday 10am.">
  <meta property="og:image" content="https://example.co.uk/share/winter-lan-v1.jpg">
  <meta property="og:image:width" content="1200">
  <meta property="og:image:height" content="630">
  <meta name="twitter:card" content="summary_large_image">
  <link rel="alternate" type="application/json+oembed"
        href="https://example.co.uk/oembed/winter-lan.json"
        title="Winter LAN weekend">
</head>

A few details matter more on Discord than elsewhere. theme-color is a name= meta tag and wants a six-digit hex value; named colours and rgb() values are less dependable. Open Graph uses property=, Twitter card tags use name=, and mixing them up is a common reason a tag is ignored. Put all of this high in the head, ahead of big inline scripts, so a crawler reading the top of the document finds it quickly.

oEmbed: author and provider lines

oEmbed is a small JSON file, linked from the page head, that describes the page for services that embed it. Discord reads a linked oEmbed file and can use a few of its fields to add lines that Open Graph alone does not provide, most usefully an author line above the headline. It is optional, and plenty of good embeds never use it, but it is the tidy way to credit an organiser, a writer or a channel.

{
  "version": "1.0",
  "type": "link",
  "title": "Winter LAN weekend",
  "author_name": "Northgate Gaming Society events team",
  "author_url": "https://example.co.uk/events",
  "provider_name": "Northgate Gaming Society",
  "provider_url": "https://example.co.uk/"
}

Serve the file with Content-Type: application/json, over https, with no login in front of it. If Discord cannot fetch or parse it, it simply falls back to the Open Graph tags, so a broken oEmbed file rarely kills the whole card, but it can leave you wondering why the author line never appears.

Server, channel and user settings that hide embeds

A perfectly tagged page can still post without a card, because Discord lets servers, moderators and individual members decide whether embeds appear. Before you touch your site, rule these out.

What you noticeLikely causeWhere to look
Links from one role or one member never get a cardThe Embed Links permission is off for that roleServer Settings, Roles, then the role's text permissions
No cards in one channel, cards everywhere elseA channel permission override denies Embed LinksThe channel's Edit Channel, Permissions screen
One person sees no cards from any siteThat member has switched off embeds and link previewsTheir User Settings, in the Chat section
The card was there, then vanishedThe poster removed it, or a moderator with Manage Messages suppressed itMessage history; ask the moderators
A single link posted with no card on purposeThe address was wrapped in angle brackets, like <https://example.co.uk>The message text itself

Permissions stack in Discord: a role can allow Embed Links while a channel override denies it, and the channel override wins for that channel. If you administer the server, the quickest check is to view the channel as the affected role using the permission preview, then paste a link that is known to embed, such as a large news site's article. If that link also shows no card, the problem is Discord-side, not your page.

๐Ÿ’ก A suppressed embed stays suppressed. Fixing your page will not bring back the card on that message; post the link again in a new message.

Discord embed caching and how to get a fresh card

Discord keeps the embed it builds for an address and reuses it when the same address is posted again. Discord does not publish how long that lasts, and in practice it can be long enough to be annoying. There is no "fetch again" button of the kind Facebook's Sharing Debugger offers, so you work around the cache rather than clearing it.

  1. Clear your own caches first. Purge the page and the share image in your CDN and any page-cache plugin. If Discord re-fetches and your CDN still hands it the old HTML, nothing changes.
  2. Change the address slightly. Discord keys its cache on the exact URL, so https://example.co.uk/events/winter-lan?e=2 is treated as a brand-new link and fetched fresh.
  3. Rename a changed image. Discord's media proxy may keep the old picture for the old image URL. Ship the new graphic as winter-lan-v2.jpg and update og:image, rather than overwriting winter-lan-v1.jpg.
  4. Repost, do not edit. Messages already sent keep the card they were created with. Editing the message text is not a dependable way to rebuild it; a new message is.

Keep the canonical tag on the page pointing at the clean address, so the query-string version used for Discord is not seen by search engines as a separate page.

Bots and webhooks: custom embeds and switching them off

Links posted by a bot or a webhook are embedded from your page's tags in the same way as a member's link. A bot can also skip the page tags altogether and send its own embed object, which is how announcement bots, shop notifications and game-server status messages get a fully controlled card. In that object the strip colour is a decimal number rather than a hex string: #2e8b57 becomes 3050327.

curl -X POST "https://discord.com/api/webhooks/WEBHOOK_ID/WEBHOOK_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Booking is open for the winter LAN.",
    "embeds": [{
      "title": "Winter LAN weekend: 40 seats",
      "url": "https://example.co.uk/events/winter-lan",
      "description": "Beginners bracket Saturday 10am. Book by 7 February.",
      "color": 3050327,
      "image": { "url": "https://example.co.uk/share/winter-lan-v1.jpg" }
    }]
  }'

To post a link with no card at all, a bot can set the message's suppress-embeds flag (flag value 4) when it sends the message, or a member can wrap the address in angle brackets. That is handy for changelog posts that list many links, where a stack of cards would bury the text.

โš ๏ธ A webhook address is effectively a password for posting into that channel. Keep it in a secret store or environment variable, never in a public repository or front-end code.

The fix, step by step

Step 1

Post a test link and read the embed

Paste the address into a private test channel and write down exactly what the card shows: strip colour, provider line, headline, snippet, and whether the picture is wide, a thumbnail or absent. Then run the same address through the Social Preview Tester to see the tags the page is sending. Comparing the two tells you whether the gap is in your markup, your server or Discord itself.

Step 2

Confirm Discordbot gets a 200

Straight after posting, search the access log for Discordbot and note the status code for the page and the image. A missing line points at a CDN or hosting firewall; a 403, 429 or 503 points at bot protection or rate limiting on your own stack. Add a narrow allow or skip rule for the Discordbot user agent on public pages and the share-image folder.

Step 3

Serve the embed tags in the raw HTML

Run curl -s against the address and check the returned head, not the browser inspector, which shows the page after scripts. You need exactly one each of og:title, og:description, og:image and og:site_name, all with absolute https addresses. If a framework adds them in the browser, move them into server rendering or pre-rendering, and delete duplicates left by a second SEO plugin.

Step 4

Set theme-color and the large image layout

Add <meta name="theme-color"> with a six-digit hex brand colour so the strip is not left neutral, and twitter:card set to summary_large_image so the picture runs full width. Point og:image at a public JPG, PNG, GIF or WebP around 1200 by 630 pixels that loads with no cookies and returns an image content type. Optionally link an oEmbed file for an author line.

Step 5

Check Embed Links and suppression

If the markup and log are fine, look at Discord itself. Confirm the posting role has the Embed Links permission and that no channel override denies it, make sure the address was not wrapped in angle brackets, and ask a second member to look, since anyone can turn embeds off in their own Chat settings. Remember that a card removed by the poster or a moderator will not return.

Step 6

Re-embed with a fresh URL

Purge the page and image from your CDN and page cache, rename the share image if you changed it, then post the address with a small query string such as ?e=2 so Discord fetches it as a new link. Check the new card on desktop and on a phone, then repost the link in a fresh message wherever the outdated card is still showing to members.

Platform notes and keeping embeds healthy

WordPress

Yoast SEO, Rank Math and similar plugins output Open Graph and Twitter card tags, but they do not all add theme-color. Add it once in your theme's header or with a small snippet hooked to wp_head. Check that only one plugin is writing og: tags; a theme and a plugin both doing it is the classic cause of Discord picking the wrong picture. Security plugins with "block fake bots" options deserve a look whenever the log shows 403s for Discordbot.

Shopify

Most themes print Open Graph tags from theme.liquid through a social meta snippet, using the product's featured image. theme-color is often missing; add it to the head in theme.liquid. Product cards on Discord use the first product image, so make sure it is a landscape-friendly shot rather than a tall cut-out that gets cropped awkwardly.

Static sites and frameworks

Hugo, Eleventy and Jekyll templates can emit every tag at build time, which Discordbot sees perfectly. With React, Vue or Svelte, use the framework's server rendering or static generation (for example Next.js metadata, Nuxt's head handling or SvelteKit's server-rendered head) so the tags are in the first response, not added after hydration.

Habits that prevent repeat problems

Questions and answers

1. Why does my link post in Discord with no embed at all?

Start with your access log straight after posting. If no Discordbot request appears, a CDN, web application firewall or hosting bot filter stopped it before your server. If it appears with a 403, 429 or 503, your own security settings refused it. If it shows a 200, look at the raw HTML for og:title and og:description, then check the channel's Embed Links permission, angle brackets around the link and the member's own embed setting.

2. How do I change the coloured line on the side of a Discord embed?

The strip comes from the theme-color meta tag in the page head. Add a tag with name set to theme-color and content set to a six-digit hex colour such as #2e8b57. Without it Discord uses its neutral default. Because Discord caches embeds, you will not see the new colour on a link that was already posted, so test with the address plus a small query string. Bots sending their own embed object set the colour as a decimal number instead.

3. How do I make Discord show a big image instead of a small thumbnail?

Add a twitter:card meta tag with the value summary_large_image. Discord reads that tag to choose the layout and then shows the og:image full width under the snippet. Also check the image itself: a landscape picture around 1200 by 630 pixels suits the large layout, while a small or square image can still look like a thumbnail. After changing the tags, post the address with a query string so Discord builds a fresh card.

4. How long does Discord cache link embeds, and can I clear it?

Discord does not publish the length of its embed cache, and there is no public tool to clear it for a given address. The practical workaround is to purge your own CDN and page cache, then share the address with a small query string such as ?e=2, which Discord treats as a new link. Rename a changed share image as well, because Discord's media proxy may keep serving the old picture from the old image address.

5. What is the Embed Links permission and who needs it?

Embed Links is a Discord permission that decides whether links a member posts are turned into cards. It is set per role in Server Settings and can be overridden per channel, and a channel override that denies it takes effect for that channel even if the role allows it. If links from certain roles or in certain channels never get a card while the same link works elsewhere, a server admin should check that permission before anyone edits the website.

6. Can I add an author line to my Discord embeds?

Yes. Link an oEmbed JSON file from the page head using a link element with rel alternate and type application/json+oembed. Discord can use the author_name and author_url fields from that file to show an author line above the headline, and provider_name for the small provider line. Serve the file over https as application/json with no login. If Discord cannot read it, the card simply falls back to the Open Graph tags on the page.

7. Why does my Discord embed show old text after I updated the page?

Discord reuses the card it built the first time that exact address was posted, and messages already sent keep their original card. Your CDN or page cache may also still be serving the old HTML to Discordbot. Purge those caches, then post the address again with a small query string so Discord fetches it fresh. Keep the canonical tag pointing at the clean address so search engines do not index the versioned link separately.

See how your link will look before you post it

The Social Preview Tester fetches your page and shows how the card is likely to look on Facebook, LinkedIn, X, WhatsApp, iMessage, Slack and Discord, side by side, so you can fix problems before anyone shares the link.

Open Social Preview Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Social Preview Fixes  ·  Social Preview Guide  ·  Slack Link Previews  ·  X / Twitter Link Previews  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.