Checking If a Media URL Is Still Valid Before You Try to Download It
I maintain a small tool called Twitter Video Download that lets people save X (Twitter) videos, pull the audio out as MP3, and download recorded Spaces. One recurring headache with any "download this media URL" tool is that the URL you get back from a page is often not the final, stable file location — it's a redirect, a signed link with an expiry, or a CDN URL that returns a different status depending on region or headers. Here's a small pattern I use to validate a media URL before committing to a full download.
The naive approach (and why it breaks)
The obvious first attempt is just:
const res = await fetch(mediaUrl);
if (res.ok) {
// stream it to disk / to the client
}
This works until it doesn't:
- Some CDNs reject a plain
fetchwithout aRangeheader and return416or403. - Signed URLs can be time-limited; by the time a user clicks "download" the link may have expired.
- A
200response doesn't guarantee the body is actually media — some endpoints return an HTML error page with a200status (looking at you, a few ad-supported hosts).
A more defensive check
async function isDownloadable(url) {
try {
const head = await fetch(url, { method: 'HEAD', redirect: 'follow' });
if (head.ok) {
const type = head.headers.get('content-type') || '';
return type.startsWith('video/') || type.startsWith('audio/');
}
} catch {
// some servers don't implement HEAD at all — fall through to a ranged GET
}
// Fallback: ask for just the first byte instead of the whole file
const partial = await fetch(url, {
method: 'GET',
headers: { Range: 'bytes=0-0' },
});
return partial.ok || partial.status === 206;
}
A few things this buys you:
-
HEAD first, GET as fallback. Plenty of media servers don't support
HEADcleanly, so treat a thrown error or a non-2xx as "try the ranged GET" rather than an immediate failure. -
Content-Typeas a sanity check, not proof. It's cheap to check and catches the "this is actually an HTML error page" case, but don't rely on it exclusively — some servers mislabel it. - A 1-byte range request is enough to confirm the server will actually serve the file without pulling the whole thing over the wire, which matters when you're validating a batch of links before offering them to a user.
Why this matters for a downloader tool specifically
When the whole point of the product is "paste a link, get a file," the worst failure mode is a user waiting on a spinner for a link that was already dead. Validating up front — even with a cheap HEAD/ranged-GET check — turns a silent timeout into an immediate, honest "this link isn't available anymore, try re-copying it from the original post" message. That single check has probably saved more support emails than any other few lines of code in the project.
Nothing here is specific to X/Twitter — the same pattern applies to any tool that consumes a media URL from a third party rather than hosting the file itself.