Nginx Hotlink Protection Without valid_referers

Nginx Hotlink Protection Without valid_referers

If Nginx reports unknown directive "valid_referers", check the compiled HTTP referer module before changing the rule. When that module is unavailable, a carefully scoped $http_referer check can deter cross-site image and video embedding. Test the configuration and three request paths before reloading Nginx.

This is bandwidth protection, not authentication. The Referer header is optional and can be omitted or forged. Use this method to reduce casual hotlinking, not to protect private files or enforce user access.

What does the unknown directive error mean?

The native Nginx directives valid_referers and $invalid_referer come from the HTTP referer module. A configuration such as this is valid only when the running Nginx build includes that module:

valid_referers none blocked server_names *.example.com example.com;

if ($invalid_referer) {
    return 403;
}

If Nginx refuses the directive, do not assume the syntax is wrong. First inspect the binary that is actually running:

nginx -V 2>&1 | grep -- '--without-http_referer_module'

If the command prints --without-http_referer_module, the module was disabled when this Nginx build was compiled. If it prints nothing, inspect the complete output from nginx -V 2>&1 and the active configuration before deciding what is wrong. The referer module is normally built into Nginx, rather than installed as a separate module that you can repair with load_module.

How should you protect media when the module is unavailable?

The fallback is to inspect the raw HTTP request header directly. Keep the rule in the relevant Nginx server context and merge it with the site’s existing static-media location. A competing regular-expression location can change caching, headers, or routing if it replaces an existing block.

location ~* .(jpg|jpeg|png|gif|webp|svg|avif|ico|mp4)$ {
    # Empty Referer (direct traffic) is allowed.
    # Block HTTP(S) Referers outside example.com and its subdomains.
    if ($http_referer ~* "^https?://(?!(?:[a-z0-9-]+.)*example.com(?::[0-9]+)?(?:[/?#]|$))") {
        return 403;
    }

    # Keep the site's existing static-file serving/caching rules as appropriate.
}

The negative lookahead allows the bare domain and subdomains such as media.example.com. It also allows an optional port and a URL path. The rule matches HTTP and HTTPS referers outside that domain. An empty Referer is allowed, so people who open an image directly are not blocked by this particular check.

That empty-header choice is deliberate. Browsers, privacy tools, mobile applications, and security software may omit the header. Blocking missing Referer values creates false positives and is not a reliable security boundary.

When should you use the native Nginx version?

If the module is present, the native directives are easier to read and less dependent on a regular expression:

location ~* .(jpg|jpeg|png|gif|webp|svg|avif|ico|mp4)$ {
    valid_referers none blocked server_names *.example.com example.com;

    if ($invalid_referer) {
        return 403;
    }
}

none permits requests without a Referer. blocked permits requests where the header exists but has been removed by a privacy filter or proxy. The site name and wildcard cover same-site and subdomain requests.

Do not copy this location block over an existing media location without checking what the old block does. Static files may already have cache headers, access rules, image processing, or special video handling. Add the smallest possible condition to the active configuration, or combine the conditions in one location.

How do you validate the rule safely?

Test the configuration before asking Nginx to reload:

sudo nginx -t && sudo systemctl reload nginx

The first command parses the configuration. The reload happens only if the test succeeds. If the test fails, the shell does not run the reload because of &&. Read the error, restore the previous configuration if necessary, and run the test again.

After a successful reload, check an existing media URL with a same-site Referer, an external Referer, and no Referer:

curl -I -e 'https://example.com/page/' 'https://example.com/wp-content/uploads/example.jpg'
curl -I -e 'https://external.example/page/' 'https://example.com/wp-content/uploads/example.jpg'
curl -I -H 'Referer:' 'https://example.com/wp-content/uploads/example.jpg'

The expected result is a normal response for the same-site request and the no-Referer request. The external HTTP(S) Referer should receive 403. Check the complete response, not just the status line. Confirm that cache headers remain appropriate and that video playback still works if the rule covers MP4 files.

What can go wrong with a hotlink rule?

  • You block legitimate users. A browser extension, privacy relay, application, or corporate proxy may remove Referer. Allowing empty headers reduces this risk.
  • You protect the wrong location. Nginx chooses locations using its matching rules. A new regular-expression location can take precedence over an existing one and change how files are served.
  • You break media delivery. MP4 playback may depend on range requests, and cached assets may depend on existing headers. Test images and video separately.
  • You treat deterrence as access control. A client can forge or omit Referer. Use authentication, signed URLs, or a suitable CDN control for protected content.
  • You trust a cache result without checking it. Test with a cache-busting query only when your cache supports that behavior, and inspect the response headers to confirm which path handled the request.

If the site’s traffic passes through Cloudflare or another CDN, decide where the policy belongs. An edge rule may stop unwanted requests before they reach the origin, while an origin Nginx rule still protects the server when the edge is bypassed. Do not assume that adding an origin rule automatically changes the CDN’s cached responses.

For WordPress sites, this is also why a PHP-only fix is usually the wrong layer. Nginx normally serves images and MP4 files without loading WordPress or PHP. A check in functions.php therefore does not protect those static files.

Can PHP protect static WordPress media?

Not with PHP alone on a typical Nginx WordPress installation. Routing every static file through PHP is technically possible, but it adds PHP-FPM load and requires careful path containment, MIME handling, caching, HEAD support, and Range support for MP4 files. It also needs protection against direct static-file bypasses.

A simple PHP readfile() proxy is not a safe production shortcut. Prefer an Nginx-only rule or a properly configured CDN or WAF hotlink control. Keep PHP focused on requests that actually need the WordPress application.

This separation is useful in broader WordPress operations too. When investigating a file or request problem, start at the layer that serves it. My guide to using auditd to trace file changes covers a different question: who changed a file on the server. For application-layer problems, see the discussion of finding the real cause of a WordPress REST API authentication error. If the site uses Cloudflare, the Cloudflare and WordPress performance guide covers a separate edge and caching concern.

Quick reference

  1. Read the error and confirm whether the referer module is compiled in with nginx -V.
  2. If it is unavailable, use a narrowly scoped $http_referer condition.
  3. Merge the rule with the existing media location instead of creating a competing one blindly.
  4. Run sudo nginx -t before sudo systemctl reload nginx.
  5. Test same-site, external, and empty-Referer requests.
  6. Check cache behavior, image delivery, and video playback.
  7. Use authentication or signed URLs when the files need real protection.

FAQ

Does valid_referers work on every Nginx installation?

No. It requires the HTTP referer module. Check the compiled binary with nginx -V instead of assuming the directive is available.

Should a hotlink rule block requests with no Referer?

Usually no. The header is optional and can be removed by browsers, privacy tools, applications, and proxies. Allowing empty headers avoids many false positives.

Is Referer validation secure access control?

No. Referer values can be forged or omitted. Use authentication, signed URLs, or a CDN/WAF feature when content must be private.

Official reference

For directive syntax and module behavior, consult the official Nginx HTTP referer module documentation.

Filed under
Aditya Shah Avatar

Written by

Hosting Support Manager & DevOps Engineer at WPMU DEV. I scale and secure WordPress infrastructure, and organize WordPress Bhopal and GDG Cloud Bhopal.

More about me · LinkedIn · GitHub