Image Copy Protection Configuration in 1C-Bitrix
Competitors copy product photos with the company's watermark — this happens constantly. Completely preventing image downloads is impossible, but you can raise the cost of copying to a level where taking your own photographs becomes easier.
What actually works and what doesn't
Does not work:
- Right-click blocking via JavaScript (
oncontextmenu="return false") — disabled in the browser in 3 seconds - CSS
pointer-events: none— the image remains accessible through DevTools - Blocking via JS
dragstarthandler — copied through screenshots or the network tab
Works:
- Watermark applied to the image server-side — difficult to remove if the mark is semi-transparent and centered
- Serving images via PHP with referer validation — slows down hotlinking
- Replacing image URLs with an authenticated resource
Watermark via GD
The most practical approach is applying a watermark when an image is uploaded to 1C-Bitrix. Handle the OnAfterFileSave event:
// /local/php_interface/init.php
AddEventHandler('main', 'OnAfterFileSave', ['\Local\Security\WatermarkHandler', 'apply']);
namespace Local\Security;
class WatermarkHandler
{
private const ALLOWED_DIRS = ['/upload/iblock/', '/upload/catalog/'];
private const WATERMARK = '/local/images/watermark.png';
public static function apply(array $file): void
{
$path = $file['PATH'] ?? '';
// Catalog images only
$inAllowed = false;
foreach (self::ALLOWED_DIRS as $dir) {
if (str_starts_with($path, $_SERVER['DOCUMENT_ROOT'] . $dir)) {
$inAllowed = true;
break;
}
}
if (!$inAllowed) return;
if (!in_array(strtolower($file['CONTENT_TYPE'] ?? ''), ['image/jpeg', 'image/png', 'image/webp'])) return;
if (!file_exists($path) || !file_exists($_SERVER['DOCUMENT_ROOT'] . self::WATERMARK)) return;
self::applyWatermark($path, $_SERVER['DOCUMENT_ROOT'] . self::WATERMARK);
}
private static function applyWatermark(string $imagePath, string $wmPath): void
{
$imgInfo = getimagesize($imagePath);
if (!$imgInfo) return;
// Load the source image
$image = match ($imgInfo[2]) {
IMAGETYPE_JPEG => imagecreatefromjpeg($imagePath),
IMAGETYPE_PNG => imagecreatefrompng($imagePath),
default => null,
};
if (!$image) return;
$wm = imagecreatefrompng($wmPath);
$imgW = imagesx($image);
$imgH = imagesy($image);
$wmW = imagesx($wm);
$wmH = imagesy($wm);
// Scale the watermark to 30% of image width if larger
if ($wmW > $imgW * 0.3) {
$ratio = ($imgW * 0.3) / $wmW;
$newWmW = (int)($wmW * $ratio);
$newWmH = (int)($wmH * $ratio);
$resizedWm = imagecreatetruecolor($newWmW, $newWmH);
imagealphablending($resizedWm, false);
imagesavealpha($resizedWm, true);
imagecopyresampled($resizedWm, $wm, 0, 0, 0, 0, $newWmW, $newWmH, $wmW, $wmH);
imagedestroy($wm);
$wm = $resizedWm;
$wmW = $newWmW;
$wmH = $newWmH;
}
// Position: center of the image
$dstX = (int)(($imgW - $wmW) / 2);
$dstY = (int)(($imgH - $wmH) / 2);
imagecopy($image, $wm, $dstX, $dstY, 0, 0, $wmW, $wmH);
// Save back
match ($imgInfo[2]) {
IMAGETYPE_JPEG => imagejpeg($image, $imagePath, 90),
IMAGETYPE_PNG => imagepng($image, $imagePath, 7),
};
imagedestroy($image);
imagedestroy($wm);
}
}
Hotlinking protection via nginx
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers none blocked ~\.yourdomain\.com;
if ($invalid_referer) {
return 403;
}
}
Or replace the image on hotlinking:
location ~* \.(jpg|jpeg|png|gif|webp)$ {
valid_referers none blocked ~\.yourdomain\.com;
if ($invalid_referer) {
rewrite ^ /local/images/hotlink-protected.jpg last;
}
}
Serving images via PHP (for private catalogs)
If images should be available to authenticated users only:
// /local/ajax/secure-image.php
$fileId = (int)($_GET['id'] ?? 0);
$token = $_GET['token'] ?? '';
if (!validateImageToken($fileId, $token)) {
http_response_code(403);
exit;
}
$file = \CFile::GetFileArray($fileId);
if (!$file) { http_response_code(404); exit; }
$path = $_SERVER['DOCUMENT_ROOT'] . $file['SRC'];
if (!file_exists($path)) { http_response_code(404); exit; }
header('Content-Type: ' . $file['CONTENT_TYPE']);
header('Cache-Control: private, max-age=3600');
readfile($path);
The token is an HMAC of the file ID and a salt: hash_hmac('sha256', $fileId, SECRET_KEY).
Practical recommendations
A watermark works best combined with thoughtful design: a semi-transparent logo centered on the image is harder to remove than a corner one. For high-value items (jewelry, designer furniture) — apply to 100% of images. For a mass catalog with thousands of SKUs — apply only to the main product image. Do not apply watermarks to brand images from suppliers — contracts prohibit it.







