tradeit.gg
Back to Operations

CDN Hotlink Protection — Investigation & Result

  Research Jun 22, 2026 cdn.tradeit.gg · CloudFront · cost analysis
Result: We set out to lock cdn.tradeit.gg to same-origin / block hotlinking. It turns out that's both unworkable (our Referrer-Policy: same-origin strips the Referer on ~99% of our own traffic) and not worth it — a full week of access logs shows genuine third-party hotlinking is 0.19% of bytes ≈ ~$0.17/month. The CDN was left open, as the data justifies.
10.0M
requests analyzed (7 days)
0.19%
bytes from 3rd-party hotlinks
~$0.17
monthly hotlink cost
98.8%
our traffic sends no Referer
The Question

"How do we set our AWS CDN to only allow same-origin host?" — i.e. stop other websites from embedding our skin images and making us pay the egress. A reasonable instinct: a public, unauthenticated CDN means anyone can hotlink it on our dime.

What the CDN Actually Is

First we mapped what CloudFront does before serving a byte. Distribution EKDMIU4PYOLZ8 (cdn.tradeit.gg) fronts a fully private S3 bucket tradeit-prepared-skins (eu-west-1) via Origin Access Control — the bucket rejects everyone except this one distribution. CloudFront also injects, invisibly, three things that live in the CDN and not in S3:

Dual-purpose CDN. The same distribution serves in-app skin images (csgo/...) and public assets that are meant to be hotlinked — Open Graph share images and SEO/insights-page images under the seo/... prefix (served to Facebook, Discord, Google Image indexing). Any blanket lockdown would break social previews and image SEO.
The Attempt — A Referer Check

There's no hotlink-protection checkbox on CloudFront (that's a Cloudflare feature). It's custom logic either way. We compared the two mechanisms at our ~44M req/mo:

OptionCost / moNotes
CloudFront Function~$4.40$0.10/M, no base fee. Chosen.
AWS WAF~$32.50$5 ACL + $1 rule + $0.60/M. ~7× pricier for the same check.

We wrote a CloudFront Function (viewer-request) that allowed only requests whose Referer/Origin host was tradeit.gg or *.tradeit.gg, ran 8 test events (all green, including dev.tradeit.gg:3000 and spoof cases), published, and associated it to the live distribution.

The Incident
Within minutes, every image on the prod insights pages fell back to the default icon. The function was 403-ing legitimate traffic.

Root cause: the site sends Referrer-Policy: same-origin. Because cdn.tradeit.gg is a different origin from tradeit.gg, that policy strips the Referer entirely on every cross-origin request to the CDN — and <img> loads aren't CORS requests, so there's no Origin header either. The function saw empty Referer + empty Origin → 403. The test events passed only because we hand-fed them a Referer that real browsers never send.

Rolled back immediately by removing the function association. CloudFront-Function 403s are generated at viewer-request and never cached, so recovery was just config propagation — no cache poisoning.
The Measurement

Before building any defense, we quantified the actual threat. Enabled CloudFront access logging to a dedicated S3 bucket, let it run a full week, then aggregated the cs(Referer) field across 10.0M requests / 81.5 GB:

Referer classRequestsBytes
No-referer (our app / direct)98.8%97.7%
Search / crawler0.2%1.4%
First-party (explicit tradeit.gg)0.8%0.7%
Third-party hotlink0.27%0.19%
Social embed (OG images)0.0%0.0%

Top "hotlinkers" were competitor skin sites — games.tapit.gg and www.breakdown.gg — at trivial volume. Extrapolated to monthly traffic, third-party hotlinking costs ~$0.17/month. We had broken every insights-page image to defend against seventeen cents.

Lessons (Reusable)
Final State
CDN left open — no request gating

Exactly as before the investigation; the correct end state given the data.

All temporary resources removed

Hotlink CloudFront Function deleted, access logging disabled, the temporary tradeit-cdn-access-logs bucket deleted. No orphaned AWS resources.