Videos download fine, photos 403. Two CDNs, two rulebooks.
The same tool, the same account, sixty seconds apart: a Reel downloads at 1080p without complaint and a photo from the same profile returns nothing. That looks like a broken feature. It's actually two different content delivery systems with two different tolerances, and images are the strict one — partly because images are the cheap thing to scrape at scale.
They aren't the same service
Video comes off hosts shaped like scontent-lga3-2.cdninstagram.com with DASH manifests and segment URLs. Images come off paths carrying a t51 marker, and the two behave nothing alike under the same request.
Send a bare server request to a video segment and you'll often get bytes. Send the same shaped request to a t51 image path from a datacenter IP and the answer is a 403 with no body — before you've finished reading the response headers.
Same company, same session context, opposite outcomes. Anyone who has built a downloader has watched this happen and assumed they broke something.
Why the image CDN is the paranoid one
Consider what each asset costs to take. A 60-second Reel is 15-25 MB and needs a manifest walk, two streams and a mux. A profile's worth of photos is a few hundred KB each and needs one GET apiece.
Images are the bulk-scrape target. If you wanted to mirror ten thousand accounts, you'd start with images, and Meta's abuse team knows the shape of that traffic better than anyone.
So the image path gets the tighter controls: aggressive ASN filtering, signatures bound tighter to the requesting context, and a shorter tolerance for anything that doesn't look like a browser rendering a page. The counter-intuitive result is that the smallest, simplest asset on the platform is the hardest one to fetch reliably.
The Cross-Origin-Resource-Policy header Instagram shipped to the image CDN is the same instinct expressed in a different layer — that one blocks third-party sites from rendering the bytes, this one blocks non-browser clients from getting them at all.
What that means for a download button
Our photo path tries the polite version first: fetch the image server-side through the residential pool, then stream it to you with a Content-Disposition header so the file lands under a name a human chose.
When Instagram refuses that fetch — and it refuses a meaningful share of them — we don't fail the download. We hand your browser the direct CDN link and let your own connection complete it. Your residential IP is the most legitimate-looking client in this entire chain, which is the whole reason the fallback works.
The trade is visible in your downloads folder. That path delivers the file and loses the pretty filename, because the response now comes from Instagram rather than from us, carrying the CDN's storage key instead of ours.
A download with an ugly name beats a clean 500. We'd rather explain the filename than explain the failure.
Signatures that expire between the two clicks
Image URLs carry an HMAC signature and an expiry, same family as the video ones we took apart in decoding an Instagram CDN URL.
The failure mode specific to photos is timing. You parse a post, the result card renders, you get distracted, you come back and click Download twenty minutes later. The signature may have moved on — and unlike a video, where you'd notice a stalled stream, an image just 403s instantly and looks like the tool never worked at all.
Re-parsing the post costs one request and issues fresh URLs. If a photo download fails on a result card that's been sitting open, that's the first thing to try, and it resolves this case completely.
Sorting your case in about a minute
Videos work, photos fail, consistently, on every post you try — that's the CDN split described above. Nothing you can configure; re-parse and use the fallback download.
Photos worked earlier today and stopped — look at how many requests you've made. Our own limiter allows 20 requests a minute and 100 an hour per IP, and Instagram's is stricter and less transparent than ours. A pause of a few minutes fixes what more clicking will not.
One specific post fails while others from the same account work — that post is likely a carousel returning a partial child list, which is its own failure with its own writeup, or the content isn't public.
Nothing works at all, video included — that's not the CDN split, that's the tool or the account. Start at the six reasons downloaders stop working.
The part we can't fix
Private accounts and close-friends posts are outside this entirely. The image was never going to be served to a logged-out request and no fallback changes that. We don't support private content, which is a product decision rather than a technical limit.
Profile pictures at full resolution are their own case — Meta has progressively narrowed what a logged-out client can retrieve, and that narrowing is why our profile-picture tool is still marked coming soon rather than shipped and flaky.
For anything you own yourself, Instagram's own data download at help.instagram.com returns your originals at upload quality, which is better than anything any downloader can hand you. It's slow and it arrives by email. It's also the only path that gives you the file before Meta's encoder touched it.
FAQ
- Why does the photo open fine in my browser but fail to download?
- Because those are different requests. Your browser rendering the image on instagram.com carries the full page context Instagram expects. A download request — from a server, or a direct hit on the CDN URL — arrives without it and gets judged on its own. Same bytes, different gatekeeping.
- Does using a VPN help?
- Usually not, and often it hurts. Commercial VPN exit IPs sit in datacenter ranges, which is precisely the category the image CDN filters hardest. Your ordinary home connection is the more trusted client here — the opposite of the usual advice.
- Why is the downloaded photo smaller than what I saw?
- Instagram caps standard feed renditions at 1080 pixels on the long edge and serves WebP to clients that accept it. What you saw in the app was that same capped rendition scaled to your screen. The original the creator uploaded isn't exposed publicly at any URL.
- Is a failed photo download ever a problem on your end?
- Sometimes, and we'd rather say so. If our proxy pool is degraded, server-side image fetches fail more often and more of your downloads take the direct-CDN fallback. That shows up as more ugly filenames rather than as errors, which makes it easy for us to miss — one reason we now monitor the pool directly instead of assuming it's fine.