How can I determine if an image is being loaded directly via URL or through an tag on a page?
I need to differentiate between two ways of loading images in the browser:
- Direct file access via URL (for example: site/file/pic/poll/2025/10/16/500.jpg)
- Image insertion via
tag in the page’s HTML code
My current Nginx configuration runs the PHP script file.php in both cases, but I need it to execute only when the file is accessed directly. How can I determine which method was used to load the image?
Direct Access to Images via URL vs. Loading Through
Tag
Direct access to an image by URL and loading through the tag differ in HTTP request headers. To determine the loading method, you can use the Referer header, which is absent in direct file access, as well as other headers such as User-Agent and Accept. In Nginx, this can be implemented through the referer module and checking request variables.
Contents
- Difference Between Direct Access and
Loading
- Methods for Detecting Loading Method in Nginx
- Nginx Configuration Using the Referer Module
- PHP Code for Request Type Detection
- Alternative Approaches and Limitations
- Practical Implementation
Difference Between Direct Access and
Loading
When accessing an image directly via URL (for example, site/file/pic/poll/2025/10/16/500.jpg), the browser sends an HTTP request with specific characteristics that differ from the request generated by the tag.
Key differences in headers:
-
Referer header:
- Direct access: Absent or contains an empty value
- Loading through
: Contains the URL of the page where the
tag is located
-
User-Agent:
- Direct access: Contains information about the user’s browser
- Loading through
: Similarly, but may have some differences in context
-
Accept:
- Direct access: Usually
*/*or specific image types - Loading through
: Similarly, but may differ depending on the page context
- Direct access: Usually
Methods for Detecting Loading Method in Nginx
Using the Referer Module
The primary method for detecting direct access is using the referer module in Nginx. This module allows checking whether the request contains a valid Referer.
How it works:
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers none blocked server_names *.yourdomain.com;
if ($invalid_referer) {
# Request came with invalid Referer (direct access)
rewrite ^ /file.php?id=$request_uri last;
}
# Normal processing through PHP
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
}
Checking Nginx Variables
Nginx provides access to HTTP headers through variables of the form $http_<header_name>. For detection, you can use:
$http_referer- Referer header value$http_user_agent- User-Agent value$http_accept- Accept value
Example check:
if ($http_referer = "") {
# Direct access without Referer
rewrite ^ /file.php?id=$request_uri last;
}
Nginx Configuration Using the Referer Module
Basic Configuration
To implement the required functionality, you can use the following Nginx configuration:
server {
listen 80;
server_name yourdomain.com;
root /var/www/yourdomain.com;
# Image processing
location ~* ^/file/pic/.*\.(jpg|jpeg|png|gif|webp)$ {
# Allow access only from specific Referers
valid_referers none blocked server_names *.yourdomain.com;
# If Referer is invalid (direct access)
if ($invalid_referer) {
return 301 /file.php?id=$request_uri;
}
# Normal image processing
try_files $uri =404;
}
# PHP script processing
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Enhanced Configuration with Regular Expressions
For more precise determination of the image path, you can use regular expressions:
location ~* "^/file/pic/(?<path>[^/]+)/.*\.(jpg|jpeg|png|gif|webp)$" {
valid_referers none blocked server_names *.yourdomain.com;
if ($invalid_referer) {
return 301 /file.php?id=$path&file=$2;
}
try_files $uri =404;
}
PHP Code for Request Type Detection
Getting Request Information
In PHP, you can access headers through the $_SERVER superglobal array:
<?php
function isDirectImageAccess() {
// Check Referer
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
// Check User-Agent
$userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
// Check Accept
$accept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : '';
// Logic for determining direct access
$isDirect = empty($referer) ||
(strpos($accept, 'text/html') === false) ||
(strpos($userAgent, 'curl') !== false) ||
(strpos($userAgent, 'wget') !== false);
return $isDirect;
}
// Example usage
if (isDirectImageAccess()) {
// Handle direct access
$fileId = $_GET['id'] ?? '';
// Your processing logic...
header('Content-Type: image/jpeg');
readfile("/path/to/images/{$fileId}.jpg");
} else {
// Handle through <img> tag
// Return image directly or redirect
header('Location: /path/to/default/image.jpg');
}
?>
Enhanced PHP Function with Additional Checks
<?php
function detectImageAccessMethod() {
$headers = getallheaders();
$referer = $headers['Referer'] ?? '';
$userAgent = $headers['User-Agent'] ?? '';
$accept = $headers['Accept'] ?? '';
// Signs of direct access
$directAccessIndicators = [
empty($referer),
strpos($accept, 'text/html') === false,
strpos($userAgent, 'curl') !== false,
strpos($userAgent, 'wget') !== false,
strpos($userAgent, 'Postman') !== false,
strpos($userAgent, 'Python-urllib') !== false
];
// If there's at least one sign of direct access
return in_array(true, $directAccessIndicators, true);
}
// Example usage
if (detectImageAccessMethod() && isset($_GET['id'])) {
// Handle direct access
handleDirectImageAccess($_GET['id']);
} else {
// Handle through <img>
handleImageViaTag();
}
?>
Alternative Approaches and Limitations
Limitations of the Referer Method
- Reliability: Referer can be forged or disabled in browsers
- Security: Some browsers and extensions may block Referer transmission
- Performance: Referer checking adds small overhead
Alternative Methods
1. Using JavaScript to Set Headers
// On pages with images
document.querySelectorAll('img[data-image-id]').forEach(img => {
img.addEventListener('load', function() {
fetch('/track-image-access.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Image-Access': 'img-tag'
},
body: JSON.stringify({
imageId: this.dataset.imageId
})
});
});
});
2. Using Sessions and Cookies
<?php
// In the script generating the page
session_start();
$_SESSION['page_with_images'] = true;
// In the image handler
function isFromImageTag() {
return isset($_SESSION['page_with_images']) && $_SESSION['page_with_images'];
}
?>
3. Processing Through a Separate Endpoint
location ~* ^/file/pic/.*\.(jpg|jpeg|png|gif|webp)$ {
# Redirect to PHP handler for all requests
rewrite ^ /image-handler.php?path=$request_uri last;
}
location = /image-handler.php {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/image-handler.php;
}
Practical Implementation
Comprehensive Solution
Here’s an example of a comprehensive Nginx and PHP configuration to solve the problem:
Nginx Configuration:
server {
listen 80;
server_name yourdomain.com;
root /var/www/yourdomain.com;
# Image processing with Referer check
location ~* "^/file/pic/(?<image_path>[^/]+)/.*\.(jpg|jpeg|png|gif|webp)$" {
# Check Referer
valid_referers none blocked server_names *.yourdomain.com;
if ($invalid_referer) {
# Direct access - redirect to PHP handler
return 301 /file.php?image=$image_path&direct=1;
}
# Normal image processing
try_files /images/$image_path.$2 =404;
}
# PHP script processing
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
PHP Handler:
<?php
// file.php
function handleImageRequest() {
$imagePath = $_GET['image'] ?? '';
$isDirect = isset($_GET['direct']) && $_GET['direct'] === '1';
if ($isDirect) {
// Handle direct access
$imageFile = "/var/www/yourdomain.com/images/{$imagePath}.jpg";
if (file_exists($imageFile)) {
// You can add logging or additional processing
logDirectAccess($imagePath);
header('Content-Type: image/jpeg');
header('Content-Length: ' . filesize($imageFile));
readfile($imageFile);
exit;
} else {
http_response_code(404);
echo "Image not found";
exit;
}
} else {
// Handle through <img> tag
header('Location: /images/' . $imagePath . '.jpg');
exit;
}
}
function logDirectAccess($imagePath) {
$logFile = '/var/log/nginx/direct_access.log';
$timestamp = date('Y-m-d H:i:s');
$ip = $_SERVER['REMOTE_ADDR'];
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$logEntry = "[$timestamp] Direct access to: $imagePath | IP: $ip | UA: $userAgent\n";
file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
// Start processing
handleImageRequest();
?>
Additional Recommendations
- Caching: Add caching headers for images
- Security: Check file access permissions
- Logging: Keep logs of direct accesses for analysis
- Protection: Limit request frequency to prevent abuse
# Adding caching headers
location ~* \.(jpg|jpeg|png|gif|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
# ... rest of the configuration
}
Sources
- How to Prevent Direct Access to Images in NGINX - Fedingo
- How to redirect user if direct access image files by browser? [nginx] - Server Fault
- Nginx: Prevent direct access to static files - Stack Overflow
- Prevent nginx from serving content to external domains (hotlinking) - Claud.io
- How to redirect if user direct access images with nginx? — LowEndTalk
- Managing request headers | NGINX
- nginx blocking direct access images - TechExpert
- nginx.org - ngx_http_referer_module
- Servers for Hackers - Mapping Headers in Nginx
- O’Reilly - Request headers - Nginx HTTP Server
Conclusion
To determine the method of loading an image (direct access via URL or through an tag), you can use the following approaches:
-
Primary method: Use the referer module in Nginx to check the Referer header. In direct access, this header is absent or contains an invalid value.
-
Additional checks: Combine Referer checking with analysis of other headers (User-Agent, Accept) to improve detection accuracy.
-
PHP processing: In PHP scripts, check headers through the
$_SERVERsuperglobal array and implement different processing logic depending on the request type. -
Security: Keep in mind that Referer can be forged, so always add additional checks and validation.
-
Performance: Optimize the Nginx configuration by adding caching and minimizing the number of checks for normal requests through
tags.
Implementing the proposed methods will allow you to effectively distinguish between the two image loading methods and run a PHP script only when directly accessing files.