Discord Embed Not Showing: Fixing Link Embeds
Discord turns a pasted link into an embed: a card with a coloured strip on its left edge, a small provider line, a linked headline, a snippet of text and a picture or thumbnail. People running community servers, game studios, shops and clubs notice fast when that card is blank, grey or out of date.
The causes are specific to Discord: its crawler, Discordbot, being turned away, tags it cannot find in the raw HTML, a missing theme-color, channel permissions, suppressed embeds and a stubborn embed cache. This guide works through each one so your next link posts with the card you designed.
- What each part of a Discord embed comes from
- How Discordbot fetches your page
- A head section Discord reads cleanly
- oEmbed: author and provider lines
- Server, channel and user settings that hide embeds
- Discord embed caching and how to get a fresh card
- Bots and webhooks: custom embeds and switching them off
- The fix, step by step
- Platform notes and keeping embeds healthy
- 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 embed | Where Discord gets it | If 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 file | No provider line appears. |
| Author line | author_name and author_url from an oEmbed file | No author line; ordinary pages rarely have one. |
| Headline | og:title, then twitter:title, then the <title>
element | A generic site-wide title, or the embed is skipped. |
| Snippet under the headline | og:description, then twitter:description
or the meta description | Headline only, or boilerplate from your theme. |
| Picture | og:image (or twitter:image), sized by twitter:card | No picture, or a small square thumbnail on the right. |
| Playable video | og:video with its type, width and height | A 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.
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.
- No JavaScript runs. Discordbot parses the HTML that arrives over the wire. Tags injected by a client-side framework, a tag manager or a consent script never reach it.
- No cookies, no session. Whatever an anonymous first-time visitor gets is what Discord gets. A cookie banner that replaces the page, a members-only redirect or a geo block all land on the card.
- The picture travels separately. Discord pulls the image down and re-serves it to members from its own media proxy, so the image URL has to be reachable on its own and send an image content type.
- Speed matters. If the origin takes several seconds to answer, Discord abandons the fetch and posts the bare link.
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.
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.
- Static sites: write one JSON file per shareable page, or a single endpoint that takes the page address as a parameter.
- WordPress: core already publishes an oEmbed discovery link for posts and pages.
Its
author_nameis the post author's display name, so check that name is one you are happy to show in every Discord server your posts reach. - Video pages: if you want a playable embed, a correct
og:videoset (a direct https file or player address, with type, width and height) is the usual route; test it, because Discord decides for itself which sources it will play inline.
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 notice | Likely cause | Where to look |
|---|---|---|
| Links from one role or one member never get a card | The Embed Links permission is off for that role | Server Settings, Roles, then the role's text permissions |
| No cards in one channel, cards everywhere else | A channel permission override denies Embed Links | The channel's Edit Channel, Permissions screen |
| One person sees no cards from any site | That member has switched off embeds and link previews | Their User Settings, in the Chat section |
| The card was there, then vanished | The poster removed it, or a moderator with Manage Messages suppressed it | Message history; ask the moderators |
| A single link posted with no card on purpose | The 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.
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.
- 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.
- Change the address slightly. Discord keys its cache on the exact URL, so
https://example.co.uk/events/winter-lan?e=2is treated as a brand-new link and fetched fresh. - 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.jpgand updateog:image, rather than overwritingwinter-lan-v1.jpg. - 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.
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
- Test every important link in your private channel before it goes into a busy server, because an announcement's card cannot be refreshed once hundreds of members have seen it.
- Version share images in the file name from the start (
-v1,-v2) so a redesign never fights Discord's media proxy. - Keep the Discordbot allow rule documented alongside your firewall settings, so a future security tidy-up does not quietly remove it.
- Set a site-wide fallback share image and
theme-colorin your template, so pages without their own graphic still post a finished-looking card.
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.
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 โ