NeuroAgent

How to determine the image loading method in Nginx

Learn how to distinguish between direct image loading via URL and through the <img> tag in Nginx. Optimize your configuration for efficient request processing.

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:

  1. Direct file access via URL (for example: site/file/pic/poll/2025/10/16/500.jpg)
  2. 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?

NeuroAgent

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

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:

  1. Referer header:

    • Direct access: Absent or contains an empty value
    • Loading through : Contains the URL of the page where the tag is located
  2. User-Agent:

    • Direct access: Contains information about the user’s browser
    • Loading through : Similarly, but may have some differences in context
  3. Accept:

    • Direct access: Usually */* or specific image types
    • Loading through : Similarly, but may differ depending on the page context

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:

nginx
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:

nginx
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:

nginx
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:

nginx
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
<?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
<?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

  1. Reliability: Referer can be forged or disabled in browsers
  2. Security: Some browsers and extensions may block Referer transmission
  3. Performance: Referer checking adds small overhead

Alternative Methods

1. Using JavaScript to Set Headers

javascript
// 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
<?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

nginx
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:

nginx
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
<?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

  1. Caching: Add caching headers for images
  2. Security: Check file access permissions
  3. Logging: Keep logs of direct accesses for analysis
  4. Protection: Limit request frequency to prevent abuse
nginx
# Adding caching headers
location ~* \.(jpg|jpeg|png|gif|webp)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
    
    # ... rest of the configuration
}

Sources

  1. How to Prevent Direct Access to Images in NGINX - Fedingo
  2. How to redirect user if direct access image files by browser? [nginx] - Server Fault
  3. Nginx: Prevent direct access to static files - Stack Overflow
  4. Prevent nginx from serving content to external domains (hotlinking) - Claud.io
  5. How to redirect if user direct access images with nginx? — LowEndTalk
  6. Managing request headers | NGINX
  7. nginx blocking direct access images - TechExpert
  8. nginx.org - ngx_http_referer_module
  9. Servers for Hackers - Mapping Headers in Nginx
  10. 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:

  1. 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.

  2. Additional checks: Combine Referer checking with analysis of other headers (User-Agent, Accept) to improve detection accuracy.

  3. PHP processing: In PHP scripts, check headers through the $_SERVER superglobal array and implement different processing logic depending on the request type.

  4. Security: Keep in mind that Referer can be forged, so always add additional checks and validation.

  5. 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.