Bug / Question: valid_referers in a custom vhost location causes image requests to fail (404) when using nginx-proxy
Summary
I'm trying to implement hotlink protection in nginx-proxy so that every virtual host automatically allows requests originating from its own domain without having to hardcode each hostname.
My expectation was that using:
valid_referers none blocked server_names;inside a custom location would use the current virtual host's server_name values, allowing me to reuse the same configuration for every hosted site.
Instead, image requests never reach the upstream and return 404s.
I'm wondering whether this is expected behavior, whether I'm missing something, or whether there is a recommended way of achieving this directly on the reverse proxy.
Environment
- nginx-proxy image:
nginxproxy/nginx-proxy:1.11.6 - nginx version:
1.31.3 - Reverse proxy in front of dedicated backend NGINX containers (one backend per WordPress site)
The generated server block looks essentially like this:
server {
server_name mydomain.com;
include /etc/nginx/vhost.d/mydomain.com;
location / {
proxy_pass http://mydomain.com;
set $upstream_keepalive true;
}
}The custom configuration is injected via:
/etc/nginx/vhost.d/mydomain.comWhat I'm trying to achieve
Ideally I'd like to have one reusable configuration like:
location ~* \.(?:gif|png|jpe?g|webp)$ {
valid_referers none blocked server_names;
if ($invalid_referer) {
return 403;
}
}so I don't have to list every hosted domain individually.
What I tested
1. Explicit domains
This works correctly:
location ~* \.(?:gif|png|jpe?g|webp)$ {
valid_referers
none
blocked
mydomain.com
*.mydomain.com;
if ($invalid_referer) {
return 403;
}
}2. Using server_names
Replacing the explicit domains with:
valid_referers none blocked server_names;causes image requests to fail.
The browser reports a 404.
3. Debugging $invalid_referer
I temporarily replaced the location with:
return 200 "host=$host
server_name=$server_name
referer=$http_referer
invalid=$invalid_referer
";Using:
curl \
--referer https://www.mydomain.com \
https://www.mydomain.com/wp-content/themes/twentytwentyfive/screenshot.pngreturned:
host=www.mydomain.com
server_name=www.mydomain.com
referer=https://www.mydomain.com
invalid=which suggests valid_referers itself is working correctly.
4. Browser request
When loading the image from the WordPress admin page the request contains:
Host: www.mydomain.com
Referer: https://www.mydomain.com/wp-admin/themes.php
Sec-Fetch-Site: same-originSo the request appears to be a valid same-origin request.
5. nginx -T
Inspecting the generated configuration showed that my custom file is included at the server level:
server {
include /etc/nginx/vhost.d/mydomain.com;
location / {
proxy_pass http://mydomain.com;
}
}Therefore my regex location becomes a sibling of the generated location /.
What I eventually discovered
It appears that the regex location:
location ~* \.(?:gif|png|jpe?g|webp)$ {
...
}takes precedence over the generated proxy location.
Since my custom location does not contain a proxy_pass, the request never reaches the upstream backend and instead results in a 404.
This seems to explain why the referer check itself succeeds while the image is never served.
Other approaches I tried
I also attempted to move the referer check into the generated location /, but discovered that:
if (...) {
valid_referers ...
}is invalid because valid_referers is not permitted inside an if block.
Current workaround
For now I've moved the hotlink protection to the backend NGINX instance that serves static files.
Each backend serves exactly one website, so I template the configuration using the official NGINX Docker image's built-in envsubst support.
For example:
valid_referers
none
blocked
${HOTLINK_DOMAIN}
*.${HOTLINK_DOMAIN};with:
HOTLINK_DOMAIN=mydomain.comThis works well, but it means the hotlink protection is implemented on every backend instead of centrally on the reverse proxy.
Question
Is there a supported way to implement this kind of reusable hotlink protection directly in nginx-proxy?
Specifically:
- Is there a way to create a custom image location that still inherits the generated
proxy_pass? - Is there a recommended way to inject
valid_referersinto the generatedlocation /without overriding it? - Is this simply a limitation of the current template architecture?
- Has anyone implemented hotlink protection centrally on
nginx-proxywhile keeping the configuration reusable across many virtual hosts?
I found the _location include mechanism documented for injecting directives into generated locations, but in my case I specifically wanted to protect only image requests without duplicating the generated proxy configuration, and I couldn't find a way to achieve that.
Source: nginx-proxy/nginx-proxy