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."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.
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:
tradeit.gg + staging + dev.tradeit.gg:3000); the bucket itself has no CORS config.max-age=2592000, public (30 days).X-Content-Type-Options: nosniff.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.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:
| Option | Cost / mo | Notes |
|---|---|---|
| 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.
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.
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 class | Requests | Bytes |
|---|---|---|
| No-referer (our app / direct) | 98.8% | 97.7% |
| Search / crawler | 0.2% | 1.4% |
| First-party (explicit tradeit.gg) | 0.8% | 0.7% |
| Third-party hotlink | 0.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.
Referrer-Policy: same-origin, ~99% of our own CDN traffic carries no Referer — indistinguishable from a hotlinker who strips it. Any such rule nukes our own traffic.csgo/... prefix (e.g. signed cookies), leave the seo/... prefix public for OG/SEO.Exactly as before the investigation; the correct end state given the data.
Hotlink CloudFront Function deleted, access logging disabled, the temporary tradeit-cdn-access-logs bucket deleted. No orphaned AWS resources.