Why Discord image links break
Open any image in Discord, copy its link, and you'll see something like cdn.discordapp.com/attachments/…/file.png?ex=…&is=…&hm=…. Those trailing parameters are a signature and an expiry timestamp. Discord started signing attachment URLs so they can only be served for a short window — the ex value is roughly 24 hours out. Inside the app you never notice, because Discord silently refreshes the link every time the message scrolls back into view. But a link you pasted somewhere else never gets refreshed — so once that window passes, the CDN rejects it and your image becomes a dead box.
This is by design, and it's not going to change: Discord is a chat app, not an image host. Its CDN is tuned for messages people are actively looking at, not for a logo embedded on your marketing site or a screenshot in a three-year-old forum thread. If your image needs to live anywhere other than inside Discord, a discordapp.com link is the wrong place to point at.
Recovering a link that already expired
Be a little wary of tools promising to "fix" Discord links — the honest mechanics matter. A signed URL that has expired can be refreshed, but only if the message still exists in Discord: reopen the original message (or copy its link again) and Discord re-serves the still-live attachment with a fresh signature. If the original message was deleted, though, the bytes are gone from Discord's CDN and no tool can conjure them back. So the reliable move isn't to recover links after they die — it's to re-host the image once, while it still loads, onto a URL that won't expire on you in the first place.
The fix: re-host on a permanent URL
The pattern is the same one that fixes expiring links everywhere — it's the identical fix for Notion's one-hour signed image URLs: fetch the image while Discord's link is valid, upload it once to storage you control, and use that URL from then on. With PixelVault it's a single API call, and the URL you get back is permanent and immutable — no signature, no expiry, served from a global CDN with zero egress fees.
If you're building a Discord bot
This is where it bites developers hardest. A bot that logs images, builds a gallery, generates art on command, or forwards attachments to a dashboard cannot store a discordapp.com URL — by the time anyone opens it, it's expired. The fix is to re-host the attachment the moment your bot sees it, then keep the permanent URL:
import { Client, GatewayIntentBits } from "discord.js";
// MessageContent is a privileged intent — needed to read attachments.
const client = new Client({ intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
] });
client.on("messageCreate", async (msg) => {
const att = msg.attachments.first();
if (!att) return;
// Pull the bytes while Discord's signed link is still valid…
const bytes = await fetch(att.url).then(r => r.blob());
// …and host them on a URL that isn't signed or time-boxed.
const form = new FormData();
form.append("file", bytes, att.name);
const res = await fetch("https://api.pixelvault.dev/v1/images", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PIXELVAULT_API_KEY}` },
body: form,
});
const { url } = (await res.json()).data;
// → a permanent https://img.pixelvault.dev/… URL, safe to store or embed.
});
client.login(process.env.DISCORD_TOKEN);
Now the URL your bot stores in a database, posts into a webhook embed, or shows on a website keeps resolving long after the original Discord message has scrolled away or been deleted. If your bot generates the image (an AI art command, a chart, a rendered card), host the generated bytes the same way; see hosting AI-generated images for that flow.
Discord CDN vs a URL you own
?w=, fmt=autoFree to start
PixelVault's free tier includes 200 MB storage, 500 uploads/month, and 1 GB bandwidth — no credit card, zero egress. Enough to back a bot or re-host the images on a small site before you pay anything, and paid plans start at $9/month. Start from the API quickstart →, or if an AI agent is doing the work, hand it the MCP server or the upload skill.