Hotlink protection for images and videos?

Hello.
How I can prevent hotlinking of video and images at server level for all websites ?

I don’t think you can do it at server level, I mean, globally for all domains changing just the main conf but you can do it modifying the template used by your sites or if you use Apache with an .htaccess file.

i tried but with no luck using custom templates:
NGINX:

server {
    listen      %ip%:%proxy_port%;
    server_name %domain_idn% %alias_idn%;
    error_log   /var/log/%web_system%/domains/%domain%.error.log error;

    include %home%/%user%/conf/web/%domain%/nginx.forcessl.conf*;

    location ~ /\.(?!well-known\/|file) {
        deny all;
        return 404;
    }

    location / {
        proxy_pass http://%ip%:%web_port%;

        location ~* ^.+\.(%proxy_extensions%)$ {
            try_files  $uri @fallback;

            root       %docroot%;
            access_log /var/log/%web_system%/domains/%domain%.log combined;
            access_log /var/log/%web_system%/domains/%domain%.bytes bytes;

            expires    max;
        }
    }

    location ~* \.(gif|jpg|jpeg|png|mp4|mkv|webm)$ {
        valid_referers none blocked mydomain.com;
        if ($invalid_referer) {
            return 403;
        }
        root %docroot%;
        access_log /var/log/%web_system%/domains/%domain%.log combined;
        access_log /var/log/%web_system%/domains/%domain%.bytes bytes;
        expires max;
    }

    location @fallback {
        proxy_pass http://%ip%:%web_port%;
    }

    location /error/ {
        alias %home%/%user%/web/%domain%/document_errors/;
    }

    include %home%/%user%/conf/web/%domain%/nginx.conf_*;
}

and in wp-content/upload htaccess:

# Begin Really Simple Security
<Files *.php>
Require all denied
</Files>
# End Really Simple Security

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTP_REFERER} !^https://(www\.)?mydomain\.com/ [NC]
    RewriteRule \.(gif|jpg|jpeg|png|mp4|mkv|webm)$ - [F,NC]
</IfModule>

<FilesMatch "\.(gif|jpg|jpeg|png|mp4|mkv|webm)$">
    Require all denied
</FilesMatch>

Edit again the template, both, tpl and stpl, and remove the block you added.

This one:

location ~* \.(gif|jpg|jpeg|png|mp4|mkv|webm)$ {
        valid_referers none blocked mydomain.com;
        if ($invalid_referer) {
            return 403;
        }
        root %docroot%;
        access_log /var/log/%web_system%/domains/%domain%.log combined;
        access_log /var/log/%web_system%/domains/%domain%.bytes bytes;
        expires max;
    }

And modify the block location ~* ^.+\.(%proxy_extensions%)$ { like this:

location ~* ^.+\.(%proxy_extensions%)$ {
        valid_referers none blocked %domain% *.%domain%;
        if ($invalid_referer) {
            return 403;
        }
    
        try_files  $uri @fallback;

            root       %docroot%;
            access_log /var/log/%web_system%/domains/%domain%.log combined;
            access_log /var/log/%web_system%/domains/%domain%.bytes bytes;

            expires    max;
        }

Once the template has been modified, rebuild your site (replace USER and DOMAIN with the actual data):

v-rebuild-web-domain USER DOMAIN yes

Thank you.
but I see only the page
Success!

Your new web server is ready to use.

custom .tpl

server {
    listen      %ip%:%proxy_port%;
    server_name %domain_idn% %alias_idn%;
    error_log   /var/log/%web_system%/domains/%domain%.error.log error;

    include %home%/%user%/conf/web/%domain%/nginx.forcessl.conf*;

    location ~ /\.(?!well-known\/|file) {
        deny all;
        return 404;
    }

    location / {
        proxy_pass https://%ip%:%web_ssl_port%;
    }

    location ~* ^.+\.(%proxy_extensions%)$ {
        valid_referers none blocked %domain% *.%domain%;
        if ($invalid_referer) {
            return 403;
        }

        try_files $uri @fallback;

        root       %docroot%;
        access_log /var/log/%web_system%/domains/%domain%.log combined;
        access_log /var/log/%web_system%/domains/%domain%.bytes bytes;

        expires    max;
    }

    location @fallback {
        proxy_pass http://%ip%:%web_port%;
    }

    location /error/ {
        alias %home%/%user%/web/%domain%/document_errors/;
    }

    include %home%/%user%/conf/web/%domain%/nginx.conf_*;
}


STPL:

server {
        listen      %ip%:%proxy_ssl_port% ssl;
        server_name %domain_idn% %alias_idn%;
        error_log   /var/log/%web_system%/domains/%domain%.error.log error;

        ssl_certificate     %ssl_pem%;
        ssl_certificate_key %ssl_key%;
        ssl_stapling        on;
        ssl_stapling_verify on;

        # TLS 1.3 0-RTT anti-replay
        if ($anti_replay = 307) { return 307 https://$host$request_uri; }
        if ($anti_replay = 425) { return 425; }

        include %home%/%user%/conf/web/%domain%/nginx.hsts.conf*;

        location ~ /\.(?!well-known\/|file) {
                deny all;
                return 404;
        }

        location / {
                proxy_pass https://%ip%:%web_ssl_port%;

        location / {
        proxy_pass https://%ip%:%web_ssl_port%;
    }

    location ~* ^.+\.(%proxy_extensions%)$ {
        valid_referers none blocked %domain% *.%domain%;
        if ($invalid_referer) {
            return 403;
        }

        try_files $uri @fallback;

        root       %docroot%;
        access_log /var/log/%web_system%/domains/%domain%.log combined;
        access_log /var/log/%web_system%/domains/%domain%.bytes bytes;

        expires    max;

        }

        location @fallback {
                proxy_pass https://%ip%:%web_ssl_port%;
        }

        location /error/ {
                alias %home%/%user%/web/%domain%/document_errors/;
        }

        proxy_hide_header Upgrade;

        include %home%/%user%/conf/web/%domain%/nginx.ssl.conf_*;
}

But did you modify the template used by your site?

I mean, if you created a template named custom, is your site using that custom template?

Hi. yes if I select custom , hestia show me an empty website with:

Success!

Your new web server is ready to use.

If i return to the default templat on hestia i see normally the website

Ok, do this. Copy the actual nginx conf, change the template to custom and compare both conf files.

Note: replace YourUser and YourDomain by the actual data:

cp /home/YourUser/conf/web/YourDomain/nginx.conf /tmp/nginx-default.conf

Now change the template to custom and once done, show me the output of this command:

diff -u /tmp/nginx-default.conf /home/YourUser/conf/web/YourDomain/nginx.conf

Hi really thank you for the help, this is the output:

--- /tmp/nginx-default.conf     2024-05-31 10:23:36.768774660 +0000
+++ /home/josef/conf/web/mydomain.digital/nginx.conf     2024-05-31 10:24:35.509242989 +0000
@@ -1,42 +1,42 @@
-#=========================================================================#
-# Default Web Domain Template                                             #
-# DO NOT MODIFY THIS FILE! CHANGES WILL BE LOST WHEN REBUILDING DOMAINS   #
-# https://hestiacp.com/docs/server-administration/web-templates.html      #
-#=========================================================================#
-
 server {
-       listen      192.168.1.120:80;
-       server_name mydomain.digital www.mydomain.digital;
-       error_log   /var/log/apache2/domains/mydomain.digital.error.log error;
+    listen      192.168.1.120:80;
+    server_name mydomain.digital www.mydomain.digital;
+    error_log   /var/log/apache2/domains/mydomain.digital.error.log error;
+
+    include /home/josef/conf/web/mydomain.digital/nginx.forcessl.conf*;
 
-       include /home/josef/conf/web/mydomain.digital/nginx.forcessl.conf*;
+    location ~ /\.(?!well-known\/|file) {
+        deny all;
+        return 404;
+    }
 
-       location ~ /\.(?!well-known\/|file) {
-               deny all;
-               return 404;
-       }
+    location / {
+        proxy_pass https://192.168.1.120:8443;
+    }
 
-       location / {
-               proxy_pass http://192.168.1.120:8080;
+    location ~* ^.+\.(jpg|jpeg|webp|gif|png|ico|svg|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|odt|ods|odp|odf|tar|wav|bmp|rtf|js|mp3|avi|mpeg|flv|html|htm|woff|woff2|ttf)$ {
+        valid_referers none blocked mydomain.digital *.mydomain.digital;
+        if ($invalid_referer) {
+            return 403;
+        }
 
-               location ~* ^.+\.(jpg|jpeg|webp|gif|png|ico|svg|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|odt|ods|odp|odf|tar|wav|bmp|rtf|js|mp3|avi|mpeg|flv|html|htm|woff|woff2|ttf)$ {
-                       try_files  $uri @fallback;
+        try_files $uri @fallback;
 
-                       root       /home/josef/web/mydomain.digital/public_html;
-                       access_log /var/log/apache2/domains/mydomain.digital.log combined;
-                       access_log /var/log/apache2/domains/mydomain.digital.bytes bytes;
+        root       /home/josef/web/mydomain.digital/public_html;
+        access_log /var/log/apache2/domains/mydomain.digital.log combined;
+        access_log /var/log/apache2/domains/mydomain.digital.bytes bytes;
 
-                       expires    max;
-               }
-       }
+        expires    max;
+    }
 
-       location @fallback {
-               proxy_pass http://192.168.1.120:8080;
-       }
+    location @fallback {
+        proxy_pass http://192.168.1.120:8080;
+    }
 
-       location /error/ {
-               alias /home/josef/web/mydomain.digital/document_errors/;
-       }
+    location /error/ {
+        alias /home/josef/web/mydomain.digital/document_errors/;
+    }
 
-       include /home/josef/conf/web/mydomain.digital/nginx.conf_*;
+    include /home/josef/conf/web/mydomain.digital/nginx.conf_*;
 }
+

But you are mixing templates tpl ↔ stpl

Create again the custom template.

cd /usr/local/hestia/data/templates/web/nginx/
cp default.tpl custom.tpl
cp default.stpl custom.stpl

Now edit custom.tpl and custom.stpl modify them to include the hotlinking protection and rebuild again your site.

Thankyou.
website rebuild ok, but the protection doesn’t work, I can include an url in another website and works and also direct access to the file it’s not denied…

Thankyou.
website rebuild ok, but the protection doesn’t work, I can include an url in another website and works and also direct access to the file it’s not denied…

I’m sorry , after cleaning the nginx cache I have verified that direct access it’s not allowed therefore “it works”, but if I first visit the website normally that will run some videos, after if I paste the direct url the content is shown, if instead i use a link of a video on the website that was not loaded previously in a normal page, the access is correctly denied!

so… how to deny direct url access also when the url was previously loaded from a legitimate web page?

If you want to deny that kind of access to your files then you must remove the none argument from valid_referers

Before:

valid_referers none blocked %domain% *.%domain%;

After:

valid_referers blocked %domain% *.%domain%;

And rebuild your site to apply the changes.

done but doesn’t work I can load the files with direct url (cleaned nginx cache, brwoser cache and tested with incognito browser session)

Please, share a link so I can test it.

Also, remember to restart nginx

yes already done

So, i have disabled cloudfalre cache with a custom rules…now are blocked (with ```
valid_referers none blocked %domain% *.%domain%;


RewriteEngine On RewriteCond %{HTTP_REFERER} !^https://(www\.)?maindomain\.com/ [NC] RewriteCond %{HTTP_REFERER} !^https://(sub\.)?otherdomain\.com/ [NC] RewriteRule \.(gif|jpg|jpeg|png|mp4|mkv|webm)$ - [F,NC]
also if I use

valid_referers blocked %domain% *.%domain%;

Editors like elementor or divi doesn’t run correctly the editing page and even i set a domain that can embed the content remotely in another website, it is always blocked…
what i mean is that RewriteCond %{HTTP_REFERER} !^https://(sub.)?otherdomain.com/ [NC] doesn’t work for the allowed remote site to include content.