Hotlink Protection — Prevent Image Leeching
Cloudflare Hotlink Protection stops other websites from embedding your images, CSS, and JavaScript files directly — forcing visitors to view your content on your site rather than leeching your bandwidth.
What You Will Learn
You will learn how hotlink protection blocks external referrers, how to configure it for specific file types, and how to allow trusted domains like search engines and social media platforms.
Why It Matters
Hotlinking costs you bandwidth and server resources every time another site displays your images. A single hotlinked image on a popular site can add terabytes of unnecessary transfer costs.
Real-World Use Case
A photography portfolio finds their high-resolution images embedded on dozens of third-party sites without permission, costing $200/month in extra CDN bandwidth. Enabling Hotlink Protection blocks all unauthorised embeds while allowing Pinterest, Google Images, and their own domain.
How Hotlink Protection Works
When a browser requests an image, it sends a Referer header indicating which page the image is displayed on. Hotlink Protection checks this header against your allowed list. Requests from non-allowed referrers receive a 403 response or a replacement image.
flowchart LR
A[Image Request] --> B{Referer header present?}
B -->|No / Missing| C[Block]
B -->|Yes| D{Referer matches allowed?}
D -->|Yes| E[Serve Image]
D -->|No| C
C --> F[403 or Replacement Image]
E --> G[Visitor Browser]
Enabling via Dashboard
- Navigate to Security > Settings.
- Find Hotlink Protection and toggle it On.
- By default, Cloudflare adds your own domain to the allowlist.
- Add additional trusted domains like
pinterest.com,google.com,facebook.com.
The feature protects three file types by default: GIF, PNG, JPEG. You can extend it to additional extensions.
API: Enable Hotlink Protection
curl -X PATCH "https://api.cloudflare.com/client/v4/zones/ZONE_ID/settings/hotlink_protection" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"value": "on"}'
Expected output:
{
"result": {
"id": "hotlink_protection",
"value": "on"
},
"success": true
}
Python: Verify Hotlink Protection is Active
import requests
def test_hotlink(zone_id, token):
url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/hotlink_protection"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
resp = requests.get(url, headers=headers)
data = resp.json()
status = data["result"]["value"]
print(f"Hotlink Protection: {status}")
return status == "on"
# Simulate a hotlink request
image_url = "https://example.com/images/photo.jpg"
hotlink_referer = {"Referer": "https://evil-site.com/page"}
resp = requests.get(image_url, headers=hotlink_referer)
print(f"Hotlink request: {resp.status_code}")
# Simulate a legitimate request
legit_referer = {"Referer": "https://example.com/gallery"}
resp2 = requests.get(image_url, headers=legit_referer)
print(f"Legitimate request: {resp2.status_code}")
Expected output:
Hotlink Protection: on
Hotlink request: 403
Legitimate request: 200
Node.js: Custom Hotlink Replacement Middleware
When Cloudflare's default 403 is not enough, serve a replacement image using a Worker:
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
const referer = request.headers.get("Referer") || "";
const allowed = ["example.com", "www.example.com", "pinterest.com"];
const isHotlink = url.pathname.match(/\.(jpg|jpeg|png|gif|webp)$/i) &&
!allowed.some((d) => referer.includes(d));
if (isHotlink) {
return new Response(
"<svg xmlns='http://www.w3.org/2000/svg' width='300' height='200'>" +
"<rect width='300' height='200' fill='%23eee'/>" +
"<text x='50%25' y='50%25' fill='%23999' font-size='20' text-anchor='middle'>" +
"Image protected by Cloudflare</text></svg>",
{
status: 200,
headers: { "Content-Type": "image/svg+xml" },
}
);
}
return fetch(request);
}
Expected behaviour: Hotlinked images display a placeholder SVG instead of the original.
Common Mistakes
| Mistake | Consequence |
|---|---|
| Not allowing Pinterest | Loses referral traffic from social pins |
| Blocking missing Referer headers | Some legitimate users (privacy mode) are blocked |
| Only protecting default extensions | WebP, AVIF, and SVG images remain unprotected |
| Not testing with incognito mode | Privacy browsers send empty Referer |
| Allowing too many domains | Defeats the purpose of protection |
Practice Questions
- What file types does Hotlink Protection cover by default?
- Why does blocking requests with no Referer header cause false positives?
- How can you serve a replacement image instead of a 403 error?
Challenge
Write a Cloudflare Worker that inspects the Referer header for all image requests and returns a custom watermark SVG for unauthorised referrers while passing through legitimate requests. Deploy and test using curl with different Referer values.
Real-World Task
Your media site has images embedded across 50 unauthorised sites. Enable Hotlink Protection, add Pinterest and Google Images to the allowlist, and verify that your own gallery pages still display images while third-party sites show a 403. Document the bandwidth savings after one week using Cloudflare analytics.
FAQ
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro — security-first tools for the modern web.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro