Image hotlink protection using rewrite with nginx

I recently setup this blog using nginx

Sometime in the past week I found that some evil sites are hotlinking my image of pink floyd album covers. Image hotlinking is when someone serves media stored on your server directly on their webpage – hence stealing bandwidth. It is not cool!

nginx makes it very easy to stop image hotlink. There are several pages on the internet which talk about it.


  • I wanted generic solution which would work for all my domains, without changing each of their configs (sites-enabled)
  • Rewrite and display a different image (Shame the hotlinker? Get free publicity?) in place of the hotlinked media
  • Must play nice with Google/Other image search engines – instead of stopping hotlink always


location ~* \.(gif|png|jpe?g)$ {
  expires 7d;
  add_header Pragma public;
  add_header Cache-Control "public, must-revalidate, proxy-revalidate";

  # prevent hotlink
  valid_referers none blocked server_names ~($host);
  if ($invalid_referer) {
    rewrite (.*) /static/images/hotlink-denied.jpg redirect;
    # drop the 'redirect' flag for redirect without URL change (internal rewrite)

# stop hotlink loop
location = /static/images/hotlink-denied.jpg { }



  • ~ is used for case sensitive matching while ~* is for case insensitive matching.
  • nginx checks locations given by regular expression in the order listed in the configuration file – more here. This means that cache headers for media and image hotlinking prevention have to be in the same block!
  • location = /static/images/hotlink-denied.jpg { } is required to prevent infinite loop that happens: Evil site requests A.jpg -> redirect request to B.jpg -> Evil site requests B.jpg -> redirect reqest to B.jpg -> ... . nginx first searches for the most specific prefix location given by literal strings regardless of the listed order – more here.
  • $host is a variable that makes the solution generic – it will serve http://$host/static/images/hotlink-denied.jpg for each domain. We just need to place different image in the same path on all domains.
  • The rewrite module always sends 302 Found response when either: [1] rewrite flag is specified [2] or when the URL being redirected to starts with http://.
    • This means that the URL shown in browser will change to the redirected URL.
    • If you want an redirect without URL change (internal rewrite, with no 302), then just drop the redirect flag in the rewrite. These two links helped me learn that.


  • Use the command nginx -s reload to reload configuration.
  • I used this tool to verify the changes. Always use an incognito window for this test, as your target image might already be in browser cache.
  • Other way of testing is using the curl command, for example curl --referer <IMAGE_URL> should show either indicate 302 redirect, or return the anti-hotlink image, while curl --referer <IMAGE_URL> should return the correct image.