Preventing image hotlinking with Nginx, with some style

Image hotlinking is a situation when your image appears on other website(s) but that image was actually being served by your web server. Image hotlinking is bad; your image gets stolen from your website and your server resources is being spent to serve them. If you happen to be running Nginx as your web server, this post describes how you can use Nginx to prevent image hotlinking with a smarter way that was suggested by Hongkiat.

Hosting an alternative image that informs web vistor of image hotlinking on a free website

There are many websites that allows us to upload an image and not restrict image hotlinking. Find one of such websites and upload an image with some wordings on hotlinking. Remember the URL of your uploaded image about image hotlinking, for example, https://sites.google.com/site/tcperpetual/home/hotlinked-message.gif.

Configuring Nginx to inspect HTTP requests made to all your images

To begin with inspecting HTTP requests for images, we first begin with the following location block:

location ~ .(gif|jpe?g|png)$ {
    # Configurations to check for image hotlinking. 
}

This location block tells our Nginx to use regular expression to determine if a HTTP request is made to an image ending with one of the following four image extensions:

  • gif
  • jpeg
  • jpg
  • png

Note that this block will not match HTTP requests directed at scripts that generate images on the fly. For example, if you have an image generation script at /generateImage.php, you will need the following block to match requests made to the image generation script:

location /generateImage.php {
    # Configurations to check for image hotlinking
}

Getting Nginx to check whether a HTTP request made to an image is a hotlink request

Hotlink requests are referred by websites that do not belong to us. Preventing image hotlinking with Nginx will require us to check whether an image HTTP request contains a valid HTTP referer value.

To check whether a HTTP request is a hotlink request in Nginx, we can include the following configurations inside the location block:

    # Remember to substitute example.com with your domain.    
    valid_referers none blocked example.com *.example.com;
    if ($invalid_referer) {
        # Remember to substitute https://sites.google.com/site/tcperpetual/home/hotlinked-message.gif 
        # with your image URL.
        return 301 https://sites.google.com/site/tcperpetual/home/hotlinked-message.gif;
    }

With this, we use the valid_referers module to check whether the HTTP request contains a valid HTTP referer value.

A HTTP request with a valid referer for our case will be either

  • not present, which is indicated by none
  • blocked, which is usually the case of the request coming from behind firewalls
  • having our domain (example.com) as the referee
  • having anything domain that ends with our domain (example.com)

After Nginx execute the valid_referers module, it will set the result of the check $invalid_referer variable. If $invalid_referer is true, we redirect the request to the image that contains the hotlink message. We do that by returning a HTTP response code of 301 alongside the image URL that contains the hotlink message.

Putting the Nginx configurations together

Putting everything together, you will get the following Nginx configurations:

location ~ .(gif|jpe?g|png)$ {
    # Configurations to check for image hotlinking. 
    
    # Remember to substitute example.com with your domain.    
    valid_referers none blocked example.com *.example.com;
    if ($invalid_referer) {
        # Remember to substitute https://sites.google.com/site/tcperpetual/home/hotlinked-message.gif 
        # with your image URL.
        return 301 https://sites.google.com/site/tcperpetual/home/hotlinked-message.gif;
    }
}

With this, we should succeed in preventing image hotlinking with Nginx.

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.