Как определить, загружается ли картинка напрямую по URL или через тег на странице?
Мне нужно различать два способа загрузки изображений в браузер:
- Прямое обращение к файлу по URL (например: site/file/pic/poll/2025/10/16/500.jpg)
- Вставка изображения через тег
в HTML-код страницы
Текущая конфигурация Nginx запускает PHP-скрипт file.php в обоих случаях, но мне нужно, чтобы он выполнялся только при прямом доступе к файлу. Как можно определить, каким образом была загружена картинка?
Прямое обращение к изображению по URL и загрузка через тег различаются по HTTP-заголовкам запроса. Для определения способа загрузки можно использовать заголовок Referer, который отсутствует при прямом доступе к файлу, а также другие заголовки, такие как User-Agent и Accept. В Nginx это можно реализовать через модуль referer и проверку переменных запроса.
Содержание
- Различие между прямым доступом и загрузкой через
- Методы определения способа загрузки в Nginx
- Конфигурация Nginx с использованием модуля referer
- PHP-код для детекции типа запроса
- Альтернативные подходы и ограничения
- Практическая реализация
Различие между прямым доступом и загрузкой через ![]()
При прямом доступе к изображению по URL (например, site/file/pic/poll/2025/10/16/500.jpg) браузер отправляет HTTP-запрос с определёнными характеристиками, которые отличаются от запроса, генерируемого тегом .
Ключевые различия в заголовках:
-
Заголовок Referer:
- При прямом доступе: отсутствует или содержит пустое значение
- При загрузке через
: содержит URL страницы, где расположен тег
-
User-Agent:
- Прямой доступ: содержит информацию о браузере пользователя
- Загрузка через
: аналогично, но может иметь некоторые отличия в контексте
-
Accept:
- Прямой доступ: обычно
*/*или конкретные типы изображений - Загрузка через
: аналогично, но может отличаться в зависимости от контекста страницы
- Прямой доступ: обычно
Методы определения способа загрузки в Nginx
Использование модуля referer
Основной метод для детекции прямого доступа - использование модуля referer в Nginx. Этот модуль позволяет проверять, содержит ли запрос корректный Referer.
Как это работает:
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers none blocked server_names *.yourdomain.com;
if ($invalid_referer) {
# Запрос пришел с некорректным Referer (прямой доступ)
rewrite ^ /file.php?id=$request_uri last;
}
# Обычная обработка через PHP
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
}
Проверка переменных Nginx
Nginx предоставляет доступ к HTTP-заголовкам через переменные вида $http_<header_name>. Для детекции можно использовать:
$http_referer- значение заголовка Referer$http_user_agent- значение User-Agent$http_accept- значение Accept
Пример проверки:
if ($http_referer = "") {
# Прямой доступ без Referer
rewrite ^ /file.php?id=$request_uri last;
}
Конфигурация Nginx с использованием модуля referer
Базовая конфигурация
Для реализации требуемой функциональности можно использовать следующую конфигурацию Nginx:
server {
listen 80;
server_name yourdomain.com;
root /var/www/yourdomain.com;
# Обработка изображений
location ~* ^/file/pic/.*\.(jpg|jpeg|png|gif|webp)$ {
# Разрешить доступ только с определённых Referer
valid_referers none blocked server_names *.yourdomain.com;
# Если Referer некорректный (прямой доступ)
if ($invalid_referer) {
return 301 /file.php?id=$request_uri;
}
# Обычная обработка изображения
try_files $uri =404;
}
# Обработка PHP-скрипта
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;
}
}
Улучшенная конфигурация с регулярными выражениями
Для более точного определения пути к изображению можно использовать регулярные выражения:
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-код для детекции типа запроса
Получение информации о запросе
В PHP можно получить доступ к заголовкам через суперглобальный массив $_SERVER:
<?php
function isDirectImageAccess() {
// Проверяем Referer
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
// Проверяем User-Agent
$userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
// Проверяем Accept
$accept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : '';
// Логика определения прямого доступа
$isDirect = empty($referer) ||
(strpos($accept, 'text/html') === false) ||
(strpos($userAgent, 'curl') !== false) ||
(strpos($userAgent, 'wget') !== false);
return $isDirect;
}
// Пример использования
if (isDirectImageAccess()) {
// Обработка прямого доступа
$fileId = $_GET['id'] ?? '';
// Ваша логика обработки...
header('Content-Type: image/jpeg');
readfile("/path/to/images/{$fileId}.jpg");
} else {
// Обработка через <img> тег
// Возвращаем изображение напрямую или перенаправляем
header('Location: /path/to/default/image.jpg');
}
?>
Улучшенная PHP-функция с дополнительной проверкой
<?php
function detectImageAccessMethod() {
$headers = getallheaders();
$referer = $headers['Referer'] ?? '';
$userAgent = $headers['User-Agent'] ?? '';
$accept = $headers['Accept'] ?? '';
// Признаки прямого доступа
$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
];
// Если есть хотя бы один признак прямого доступа
return in_array(true, $directAccessIndicators, true);
}
// Пример использования
if (detectImageAccessMethod() && isset($_GET['id'])) {
// Обработка прямого доступа
handleDirectImageAccess($_GET['id']);
} else {
// Обработка через <img>
handleImageViaTag();
}
?>
Альтернативные подходы и ограничения
Ограничения метода с referer
- Надёжность: Referer можно подделать или отключить в браузере
- Безопасность: Некоторые браузеры и расширения могут блокировать передачу Referer
- Производительность: Проверка Referer добавляет небольшие накладные расходы
Альтернативные методы
1. Использование JavaScript для установки заголовка
// На странице с изображениями
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. Использование сессий и cookies
<?php
// В PHP-скрипте, генерирующем страницу
session_start();
$_SESSION['page_with_images'] = true;
// В обработчике изображений
function isFromImageTag() {
return isset($_SESSION['page_with_images']) && $_SESSION['page_with_images'];
}
?>
3. Обработка через отдельный эндпоинт
location ~* ^/file/pic/.*\.(jpg|jpeg|png|gif|webp)$ {
# Перенаправляем на PHP-обработчик для всех запросов
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;
}
Практическая реализация
Комплексное решение
Вот пример комплексной конфигурации Nginx и PHP для решения задачи:
Nginx конфигурация:
server {
listen 80;
server_name yourdomain.com;
root /var/www/yourdomain.com;
# Обработка изображений с проверкой Referer
location ~* "^/file/pic/(?<image_path>[^/]+)/.*\.(jpg|jpeg|png|gif|webp)$" {
# Проверяем Referer
valid_referers none blocked server_names *.yourdomain.com;
if ($invalid_referer) {
# Прямой доступ - перенаправляем на PHP-обработчик
return 301 /file.php?image=$image_path&direct=1;
}
# Обычная обработка изображения
try_files /images/$image_path.$2 =404;
}
# Обработка PHP-скриптов
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 обработчик:
<?php
// file.php
function handleImageRequest() {
$imagePath = $_GET['image'] ?? '';
$isDirect = isset($_GET['direct']) && $_GET['direct'] === '1';
if ($isDirect) {
// Обработка прямого доступа
$imageFile = "/var/www/yourdomain.com/images/{$imagePath}.jpg";
if (file_exists($imageFile)) {
// Можно добавить логирование или дополнительную обработку
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 {
// Обработка через <img> тег
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);
}
// Запускаем обработку
handleImageRequest();
?>
Дополнительные рекомендации
- Кэширование: Добавьте заголовки кэширования для изображений
- Безопасность: Проверяйте права доступа к файлам
- Логирование: Ведите логи прямых обращений для анализа
- Защита: Ограничьте частоту запросов для предотвращения злоупотреблений
# Добавление заголовков кэширования
location ~* \.(jpg|jpeg|png|gif|webp)$ {
expires 30d;
add_header Cache-Control "public, immutable";
# ... остальная конфигурация
}
Источники
- 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
Заключение
Для определения способа загрузки изображения (прямой доступ через URL или через тег ) можно использовать следующие подходы:
-
Основной метод: Используйте модуль referer в Nginx для проверки заголовка Referer. При прямом доступе этот заголовок отсутствует или содержит некорректное значение.
-
Дополнительные проверки: Комбинируйте проверку Referer с анализом других заголовков (User-Agent, Accept) для повышения точности детекции.
-
PHP-обработка: В PHP-скрипте проверяйте заголовки через
$_SERVERсуперглобальный массив и реализуйте различную логику обработки в зависимости от типа запроса. -
Безопасность: Учитывайте, что Referer можно подделать, поэтому всегда добавляйте дополнительные проверки и валидацию.
-
Производительность: Оптимизируйте конфигурацию Nginx, добавляя кэширование и минимизируя количество проверок для обычных запросов через
теги.
Реализация предложенных методов позволит вам эффективно различать два способа загрузки изображений и запускать PHP-скрипт только при прямом доступе к файлам.