Performance10 min read

Hotlinking: What It Is, How to Detect It, and Whether Hotlink Protection Is Right for You

What is hotlinking, how do you detect it in your access logs, and when should you prevent image hotlinking at all? Nginx, Apache, signed URL, and CDN hotlink protection examples, plus the watermark alternative and the gotchas that block legit users.

By ImageGuide Team·Published August 21, 2026
hotlink protectionprevent image hotlinkingwhat is hotlinkingbandwidthreferercdnperformance

Someone else’s website displays your photo. The page loads from their server, but the image file loads from yours. Every visitor to their page downloads bytes from your infrastructure, and you pay the bandwidth bill. That is hotlinking, and it is one of the oldest problems on the web.

This guide covers what hotlinking actually is, how to detect it in your own logs, and — more importantly — whether you should block it at all. Blocking sounds like the obvious answer. It often is not. We will walk through server-level hotlink protection with Nginx and Apache, token-based protection, CDN options, the watermark-everything philosophy, and the ways each approach accidentally breaks legitimate users.

What Is Hotlinking?

Hotlinking is embedding an image on a page by pointing directly at the image’s URL on someone else’s server. The HTML lives on site A. The image request goes to site B. Site B serves the bytes, absorbs the bandwidth, and usually does not know it happened.

A minimal example:

<!-- Served from forum.example.com -->
<img src="https://yourdomain.com/images/sunset-4k.jpg" alt="Sunset">

When a visitor opens the forum page, their browser requests sunset-4k.jpg from yourdomain.com. Your server sees a normal image request and serves it. The forum gets free hosting for your image. You get the traffic and the bill.

People use the term loosely, so it helps to separate the cases:

Case What happens Usually abuse?
A forum embeds your product photo in a rant Your bytes serve their page Yes
A scraper mirrors thousands of your images by URL Your bytes serve their whole site Yes
Google Images shows your thumbnail inline Google serves a cached copy, not your file No
Someone links to your image to credit you One request, then browser cache No
An app fetches your public API image Intended use No

The last distinction matters because of a common misconception. When Google Images displays a result inline, it serves a cached thumbnail from Google’s own servers. That is not hotlinking. Blocking Google’s crawler to “stop hotlinking” does nothing except remove your images from search results.

Why People Care: The Bandwidth Math

The direct cost is bandwidth. A single 800 KB JPEG viewed 50,000 times from someone else’s page is about 40 GB of egress you did not plan for. On a metered host or a CDN with overage billing, that converts directly into money.

The indirect costs are quieter but real:

  • CPU and cache pressure. Every hotlinked request competes with your real visitors for cache space and worker time.
  • Skewed analytics. Referrer reports fill with domains you have never heard of.
  • Lost control. If a popular page embeds your image and you redesign your site, their page breaks, and their visitors blame you.
  • Legal exposure in reverse. Some site owners have been sued or shamed because a hotlinked image later changed to something embarrassing. You do not control the context your image appears in.

None of this matters at small scale. It matters a lot when one viral forum post or a scraper discovers your /images/ folder.

How to Detect Hotlinking

You do not need special tooling. Your access logs already contain the evidence. Every HTTP request carries a Referer header — the URL of the page that requested the asset. Hotlinked requests show your domain as the host but someone else’s domain as the referer.

Grep Your Access Logs

For a standard Nginx or Apache combined-format log, the referer is the second-to-last quoted field. These one-liners find requests where the referer is not your own domain:

# Top referring domains that are NOT your site
awk -F'"' '{print $4}' access.log \
  | grep -v 'yourdomain.com' \
  | grep -v '^-$' \
  | grep -E '\.(jpg|jpeg|png|webp|gif)' \
  | sort | uniq -c | sort -rn | head -20

Wait — that prints full referer URLs mixed with request paths. A cleaner approach filters on the request field first, then groups by referer host:

# Requests for images, grouped by external referer host
awk -F'"' '$2 ~ /\.(jpg|jpeg|png|webp|gif)/ {print $4}' access.log \
  | grep -vE '(^-$|yourdomain\.com|www\.google\.|bing\.)' \
  | sed -E 's#https?://([^/]+).*#\1#' \
  | sort | uniq -c | sort -rn | head -20

Reading that pipeline: field 2 is the request line, field 4 is the referer. We keep only image requests, drop empty referers and search engines, strip each referer down to its hostname, and count. The output is a leaderboard of who is embedding your images.

Check Bandwidth by Referer

Counting requests tells you who. Summing bytes tells you how much. If your log format includes the response size (the combined format does, right after the referer), you can total it:

# Bytes served to each external referer
awk -F'"' '$2 ~ /\.(jpg|jpeg|png|webp)/ {split($5, a, " "); print $4, a[2]}' access.log \
  | grep -vE '(yourdomain\.com|^-$)' \
  | awk '{bytes[$1] += $2} END {for (h in bytes) printf "%10.1f MB  %s\n", bytes[h]/1048576, h}' \
  | sort -rn | head -10

Watch for the Signature Pattern

Hotlinking leaves a fingerprint even without log analysis:

  • A sudden bandwidth spike with flat traffic to your own pages.
  • Image requests far exceeding page views in your analytics.
  • Referrer reports dominated by one or two unknown domains.
  • A single image file consuming disproportionate traffic.

If your host shows 200 GB of monthly transfer but your analytics report 5 GB of page traffic, something is serving images to someone else’s audience.

Caveat: Referers Lie Sometimes

The Referer header is optional. Browsers strip it in some privacy modes, and many native apps never send one. So logs undercount hotlinking from apps, and they can also misattribute requests. Treat log analysis as a strong signal, not a court verdict.

Should You Block at All?

Here is the nuance most hotlink-protection tutorials skip: an embed is sometimes the cheapest marketing you will ever get.

Arguments for allowing hotlinks:

  • A popular forum post embedding your photo is free distribution. If the image carries your watermark or a visible credit, every view is an impression.
  • Blocking referrers can break embedded images in blog platforms, aggregators, and reader apps that legitimately link to you.
  • Enforcement costs something. Every referer check is a rule that can misfire.

Arguments for blocking:

  • Scrapers and leech sites cost real money and give nothing back.
  • If you sell the image, an embed is a lost sale.
  • Uncontrolled context can embarrass you.

The pragmatic middle path that many photographers and stock sites take: serve watermarked copies to external referrers and full-quality originals to your own pages. The embed still works, the exposure still happens, but the free ride now advertises you. We cover this below, and our guide on how to watermark images covers the watermarking side in depth.

One more line to draw clearly: Google Images inline serving is not hotlinking abuse. Google caches thumbnails and serves them from its own infrastructure. If you block on missing or unusual referrers carelessly, you can break image search visibility for no bandwidth savings. If you want control over search indexing, use robots.txt and meta tags — a different tool for a different job (more on that below).

If you decide to block, the classic mechanism checks the Referer header at the web server and rejects requests for images that do not come from your own domains.

Nginx: valid_referers

Nginx implements this with the valid_referers directive plus a conditional:

location ~* \.(jpg|jpeg|png|gif|webp|avif|svg)$ {
    valid_referers none blocked server_names
        *.yourdomain.com
        yourdomain.com
        *.google.com
        *.bing.com;

    if ($invalid_referer) {
        return 403;
        # or: rewrite ^ /images/hotlink-placeholder.jpg last;
    }

    expires 30d;
    add_header Cache-Control "public, immutable";
}

What each piece does:

  • none allows requests with no Referer header at all. This matters more than it sounds — we cover why in the gotchas section. If you omit none, you block every user whose browser or app strips the header.
  • blocked allows referers that arrived but were mangled by firewalls or proxies (missing the scheme).
  • server_names allows your own configured server_name values.
  • The wildcard entries allow your domains and the search engines you want to keep happy.
  • $invalid_referer becomes true when the referer matches none of the above, and the if returns 403 Forbidden.

The commented rewrite line shows the alternative response: instead of an error, serve a placeholder image. More on that trick later.

Apache: .htaccess with mod_rewrite

On shared hosting you usually control an .htaccess file rather than the server config. The classic Apache hotlink protection block:

RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?yourdomain\.com [NC]
RewriteCond %{HTTP_REFERER} !^https?://(www\.)?google\. [NC]
RewriteRule \.(jpg|jpeg|png|gif|webp)$ - [F,NC,L]

Line by line:

  • RewriteCond %{HTTP_REFERER} !^$ — the referer must not be empty. Delete this line if you want to allow referer-less clients, which is usually the safer default.
  • The second condition allows your own domain, case-insensitive ([NC]).
  • The third keeps Google working.
  • The RewriteRule matches image extensions and sends [F] (403 Forbidden), [NC] (no case), [L] (last rule).

To serve a placeholder instead of an error:

RewriteRule \.(jpg|jpeg|png|gif|webp)$ https://yourdomain.com/images/stolen-placeholder.jpg [R,NC,L]

Why Referer Checks Are Fragile

Referer-based blocking has three structural weaknesses, and you should know them before you deploy:

  1. The header is optional. Privacy extensions, some corporate proxies, and most native mobile apps omit it. A strict block locks those users out.
  2. Referrers can be spoofed. Anyone who really wants your images sets Referer: https://yourdomain.com/ in a script. Referer blocking stops casual leeches, not determined ones.
  3. Caches complicate it. If a CDN caches a 403 response or a placeholder, real users can inherit the wrong cached copy. Make sure your cache keys and error-page caching behave.

This is why referer blocking is best understood as a leech filter: highly effective against lazy scrapers and forum embeds, ineffective against anything that tries.

Token-Based Protection (Signed URLs)

For stronger guarantees, stop trusting the referer and start trusting a cryptographic token. A signed URL carries a token that your application generates, and your storage or CDN validates before serving. No valid token, no image — regardless of what the referer says.

The concept:

Public URL:   https://cdn.yourdomain.com/photos/sunset.jpg
Signed URL:   https://cdn.yourdomain.com/photos/sunset.jpg?token=9f2c...&expires=1724284800

The token is typically an HMAC of the path plus an expiry timestamp, keyed with a secret only your server and the CDN know:

token = HMAC-SHA256(secret, path + expires)

Properties of a good scheme:

  • Expiry. expires is a Unix timestamp. Links die after it passes, so a scraped URL stops working within minutes or hours.
  • Path binding. The token covers the exact path, so it cannot be reused for other files.
  • No referer involvement. Apps, email clients, and privacy browsers all work, because the token travels in the URL itself.

The tradeoff is operational: every URL must be minted by your application. That is trivial for dynamic pages and painful for static sites. It also breaks the “paste the URL anywhere” sharing model on purpose. Signed URLs fit paywalled media, print-resolution downloads, and private galleries. They are usually overkill for a blog’s hero images.

Most CDNs and object stores ship this as a built-in feature under names like “signed URLs,” “token auth,” or “URL signing.” If you are evaluating CDNs anyway, our guide on the signs you need an image CDN covers when the move pays for itself.

If your images already sit behind a CDN, do the protection there. It is one toggle instead of a config file, it runs at the edge close to the user, and it integrates with caching correctly.

Typical CDN options, roughly in order of strength:

Mechanism How it works Blocks determined scrapers?
Referer ACL Allow-list of referrers at the edge No — spoofable
Empty-referer policy Choose to allow or block missing referers N/A (policy choice)
Token / signed URLs HMAC token validated at the edge Yes
Bot management Fingerprint and rate-limit non-browser clients Mostly

A referer ACL at the CDN behaves like the Nginx config above but applies globally and never touches your origin. Sirv, for example, includes hotlink protection among its delivery settings, so you allow your own domains and the CDN rejects other referrers before they ever consume your origin bandwidth — the Sirv documentation covers its delivery and viewer behavior, and the platform pairs protection with on-the-fly transforms, so a blocked leech never triggers an expensive resize either.

Two CDN-specific gotchas:

  • Cache the decision, not the accident. Ensure 403s and placeholder responses get short or no cache lifetimes, or one bad response sticks at the edge for hours.
  • Test with the referer absent. Most CDN dashboards let you simulate a request. Check the no-referer case explicitly, because that is the one that breaks email clients and apps.

The Watermark Alternative

There is a whole philosophy that says: do not block, brand.

The reasoning goes like this. A hotlinked image on a popular page is distribution you did not pay for. Blocking converts that distribution into nothing. A watermark converts it into advertising. The leech site does your marketing for you, and every viewer knows where the image came from.

The implementation is usually a CDN transform rather than a batch job: the same original, with a watermark composited when the request comes from an external referrer. Combined with the Nginx rewrite or a CDN rule, the flow becomes:

Referer = yourdomain.com  →  serve original
Referer = anything else   →  serve watermarked variant

This is the least destructive form of hotlink “protection” because almost nothing breaks. The image still displays on the forum. The email client still shows it. The scraper still mirrors it — with your name on every copy.

The cost is honest: watermarked copies still consume your bandwidth, and a low-opacity watermark can be cropped or edited out. It is a business decision more than a technical one. If exposure has value to you, watermark. If pure cost control is the goal, block or sign. Our watermarking guide walks through visible and invisible watermarking approaches.

The Placeholder Switcheroo

A famous variant of blocking: instead of returning an error, rewrite hotlinked requests to a replacement image. The leech site’s <img> tag keeps working, but it now displays a picture that says “This image was stolen from yourdomain.com” — or, historically, something less polite.

The mechanics are the rewrite lines you already saw:

if ($invalid_referer) {
    rewrite ^ /images/placeholder.jpg last;
}

Why it is effective: the leech cannot easily tell the difference at embed time, and their audience sees your message. Why it is ethically loaded:

  • You are rendering content on someone else’s page. In several jurisdictions this has been treated unkindly in disputes; deliberately displaying defamatory or shocking content inside someone’s site has led to lawsuits over exactly this trick.
  • Cache pollution. If the placeholder gets cached under the original URL’s key by an intermediary, your real users can receive the placeholder too. Keep the placeholder uncacheable or on a separate URL.
  • Collateral damage. A misconfigured referer rule plus a placeholder means your own pages can display the taunt. Test thoroughly.

A neutral, safe version: swap in a generic branded placeholder (“Image from yourdomain.com”) rather than anything hostile. You keep the deterrence and drop the legal and reputational risk.

Every referer-based scheme has the same failure modes. Plan for them before you deploy, not after the support tickets.

Broken thing Why Fix
Email clients (Outlook, some webmail) Proxies fetch images with no referer or their own Allow empty referers (none in Nginx)
Native mobile apps No referer concept Allow empty referers, or sign URLs
VPNs and privacy tools Strip or randomize referer Allow empty referers
RSS readers Some send their own domain as referer Allow-list major readers
Your own staging domain Different hostname, same content Add to valid_referers
AMP / instant-article copies Served from cdn.ampproject.org etc. Allow-list if you use AMP
Print-friendly services Fetch with no referer Allow empty referers

The single most important rule: almost always allow the empty referer. Blocking referer-less requests is the configuration that turns hotlink protection into an outage. The empty referer is not evidence of leeching — leech sites almost always send a referer, because the embed happens in a browser page. The clients that omit referers are overwhelmingly legitimate: apps, proxies, privacy tools, email.

Also decide deliberately about search engines. Allowing google.com, bing.com, and similar referrers keeps image search click-throughs working. Blocking them saves almost nothing (Google serves cached thumbnails) and costs discovery.

These two get confused, so let’s separate them cleanly. robots.txt is a crawler directive. It asks well-behaved bots not to fetch or index your content. It does nothing to browsers, apps, or impolite scrapers, and it does nothing about a human-run forum post.

Hotlink protection is an enforcement mechanism. It rejects requests at serve time based on referer or token, regardless of what any bot promises.

Question robots.txt Hotlink protection
Stops Google from indexing images? Yes, if respected No
Stops a forum embed? No Yes
Stops a scraper with a spoofed referer? No Not reliably
Breaks legit users if wrong? Rarely Often
Enforced by anyone? Voluntary You, at the edge

Use robots.txt when your goal is search and AI-crawler policy. Use hotlink protection when your goal is bandwidth. They solve different problems and stack fine together. If your real concern is how your images are used and attributed rather than bandwidth, our guide on image metadata privacy covers the embedded-information side of the same question.

Decision Table: Block, Allow, Watermark, or Sign?

Strategy Best when Cost Breaks legit users? Stops scrapers?
Allow Exposure has value; traffic is small None No No
Block (referer ACL) Clear leech pattern in logs; public content Low config Possible — allow empty referer Casual ones
Watermark externals Marketing value in every embed Transform setup Rarely No — but branded
Signed URLs Paid or private content; expiring access App changes for every URL By design Yes

A sensible default path for most sites:

  1. Measure first. Run the log analysis above. No measurable hotlink traffic means no action needed.
  2. Small leaks → allow, and make sure your images are compressed so the leak is cheap. Our image caching headers guide shows how long-lived caching shrinks repeat costs even from leech pages.
  3. Meaningful leeching, public content → referer-based hotlink protection at the CDN or server, empty referers allowed, search engines allowed.
  4. Brand-sensitive or viral exposure → watermarked variants for external referrers.
  5. Valuable or private files → signed URLs with expiry.

Checklist Before You Deploy

  • Confirm the leech traffic in logs (referer + byte totals), not just a hunch.
  • Decide the empty-referer policy. Default: allow.
  • Allow-list search engine referrers.
  • Allow-list your own alternate domains, staging, and email or app infrastructure.
  • Choose the response: 403, placeholder, or watermarked variant.
  • Verify cache behavior for the blocked response so a 403 never sticks in a shared cache.
  • Test from: your site, an external page, an email client, a mobile app, and a browser with referer stripping enabled.
  • Re-check your logs a week later. Confirm the leech stopped and your error rate did not move.

Wrapping Up

Hotlinking is other people’s pages loading your bytes. Detect it with referer analysis in your access logs, quantify it in megabytes, and then choose deliberately: allow it as marketing, block casual leeches with referer rules, brand it with watermarks, or close it with signed URLs. The one wrong move is the strict default — blocking every request without a referer breaks email clients, apps, and privacy tools while barely inconveniencing actual scrapers.

Protect the bandwidth, keep the legit web working, and let the decision follow your logs rather than your reflexes.

Related Resources

Format References

Ready to optimize your images?

Sirv automatically optimizes, resizes, and converts your images. Try it free.

Start Free Trial