תארו לעצמכם מעלים תמונה של 10 MB לאתר שלכם—מבקר עם גלישה סלולרית עוזב לפני שהיא נטענת. תמונות כבדות הן הגורם העיקרי ל-LCP (Largest Contentful Paint) ירוד ולשיעורי נטישה גבוהים. ראינו את זה עשרות פעמים: לקוחות מתלוננים על אתרים איטיים, אבל האשם הוא קובצי JPEG לא מותאמים עם מטא-דאטה של EXIF, כל אחד במשקל 500 KB. אופטימיזציית תמונות בצד השרת פותרת את זה מהשורש: דחיסה אוטומטית עם אובדן נתונים ובלעדיו, המרה לפורמטים מודרניים כמו WebP ו-AVIF, שינוי גודל דינמי—כל זה מפחית את העברת הנתונים ב-40–70% ללא פגיעה באיכות. אופטימיזציית תמונות משפיעה ישירות על Core Web Vitals, במיוחד LCP ו-CLS, ומשפרת את דירוגי מנועי החיפוש. באחד הפרויקטים שלנו, תהליך נכון קיצץ את ה-LCP מ-4.2 שניות ל-1.1 שניות עבור אתר מסחר אלקטרוני עם אלפי מוצרים. לפי ויקיפדיה, WebP הוא פורמט תמונה המספק דחיסה מעולה.
כיצד אופטימיזציית תמונות בצד השרת מאיצה את האתר שלכם
כאשר תמונה מועלית לשרת, תהליך רץ: ספריית Sharp או Intervention Image משנה את גודלה לרוחבים מקסימליים מוגדרים מראש (400, 800, 1200, 1920 פיקסלים), ממירה ל-WebP ו-AVIF, ומסירה מטא-דאטה של EXIF. הקבצים שנוצרים נשמרים במטמון ב-CDN עם TTL ארוך. הדפדפן מקבל קובץ קטן בפורמט נתמך—הדף נטען באופן מיידי. בניגוד לאופטימיזציה בצד הלקוח (דחיסת JavaScript), הגישה בצד השרת אינה מכבידה על המכשיר של המשתמש ועובדת היטב עבור כל התעבורה.
למה לבחור ב-WebP ו-AVIF?
| פורמט | סוג דחיסה | הפחתה ממוצעת | תמיכת דפדפנים |
|---|---|---|---|
| JPEG | עם אובדן נתונים | 1x (קו בסיס) | 100% |
| WebP | עם/בלי אובדן נתונים | 25-35% פחות מ-JPEG | 96% |
| AVIF | עם/בלי אובדן נתונים | 50% פחות מ-JPEG | 85% |
AVIF מציע דחיסה טובה יותר, WebP תאימות טובה יותר. האסטרטגיה האופטימלית היא לייצר את שניהם ולספק אותם דרך <picture> עם srcset.
הערימה הטכנולוגית שלנו לאופטימיזציה בצד השרת
בפרויקטים של Node.js אנו משתמשים בספריית Sharp—היא מספקת תפוקה גבוהה וצריכת זיכרון מינימלית. עבור יישומי PHP אנו משתמשים ב-Intervention Image עם Imagick. דוגמאות קוד נמצאות למטה.
Node.js: תהליך Sharp
import sharp from 'sharp';
interface OptimizeOptions {
maxWidth?: number;
quality?: number;
format?: 'webp' | 'avif' | 'jpeg';
}
async function optimizeImage(inputBuffer: Buffer, options: OptimizeOptions = {}): Promise<Buffer> {
const { maxWidth = 1920, quality = 80, format = 'webp' } = options;
return sharp(inputBuffer)
.resize(maxWidth, undefined, {
withoutEnlargement: true,
fit: 'inside',
})
.toFormat(format, {
quality,
effort: 4, // баланс скорость/размер
})
.withMetadata({ orientation: undefined }) // убрать EXIF rotation
.toBuffer();
}
// Middleware для lazy оптимизации
app.get('/images/:key', async (req, res) => {
const { key } = req.params;
const { w, q = '80', f = 'webp' } = req.query;
// Проверить кэш
const cacheKey = `${key}:${w}:${q}:${f}`;
const cached = await s3.getObject({ Key: `optimized/${cacheKey}.${f}` }).catch(() => null);
if (cached) {
return res.type(`image/${f}`).send(await streamToBuffer(cached.Body));
}
// Получить оригинал
const original = await s3.getObject({ Key: `originals/${key}` });
const buffer = await streamToBuffer(original.Body);
// Оптимизировать
const optimized = await optimizeImage(buffer, {
maxWidth: w ? parseInt(w as string) : 1920,
quality: parseInt(q as string),
format: f as 'webp' | 'avif' | 'jpeg',
});
// Сохранить в кэш
await s3.putObject({
Key: `optimized/${cacheKey}.${f}`,
Body: optimized,
ContentType: `image/${f}`,
CacheControl: 'public, max-age=31536000',
});
res.type(`image/${f}`).send(optimized);
});
PHP: אופטימיזציה בעת העלאה
use Intervention\Image\Facades\Image;
class ImageOptimizationService
{
const FORMATS = ['webp', 'avif'];
const SIZES = [400, 800, 1200, 1920];
public function process(UploadedFile $file): array
{
$image = Image::make($file->getPathname());
// Убрать EXIF и повернуть по ориентации
$image->orientate()->stripExif();
$paths = [];
foreach (self::SIZES as $width) {
if ($image->width() < $width) continue;
$resized = clone $image;
$resized->resize($width, null, fn($c) => $c->aspectRatio()->upsize(false));
foreach (self::FORMATS as $format) {
$quality = $format === 'avif' ? 60 : 82;
$key = "images/{$width}w/{$this->generateKey()}.{$format}";
Storage::disk('s3')->put(
$key,
$resized->encode($format, $quality)->__toString(),
['CacheControl' => 'public, max-age=31536000']
);
$paths[$format][$width] = $key;
}
}
return $paths;
}
} HTML: תמונות רספונסיביות עם srcset
// Blade хелпер для отображения оптимизированных изображений
function responsive_img(array $paths, string $alt, string $sizes = '100vw'): string {
$avifSrcset = collect($paths['avif'] ?? [])
->map(fn($path, $width) => Storage::disk('s3')->url($path) . " {$width}w" )
->join(', ');
$webpSrcset = collect($paths['webp'] ?? [])
->map(fn($path, $width) => Storage::disk('s3')->url($path) . " {$width}w" )
->join(', ');
$fallback = Storage::disk('s3')->url(end($paths['webp']));
return <<<HTML
<picture>
<source type="image/avif" srcset="{$avifSrcset}" sizes="{$sizes}">
<source type="image/webp" srcset="{$webpSrcset}" sizes="{$sizes}">
<img src="{$fallback}" alt="{$alt}" loading="lazy" decoding="async">
</picture>
HTML;
}שימו לב למאפיין import sharp from 'sharp'; interface OptimizeOptions { maxWidth?: number; quality?: number; format?: 'webp' | 'avif' | 'jpeg'; } async function optimizeImage(inputBuffer: Buffer, options: OptimizeOptions = {}): Promise<Buffer> { const { maxWidth = 1920, quality = 80, format = 'webp' } = options; return sharp(inputBuffer) .resize(maxWidth, undefined, { withoutEnlargement: true, fit: 'inside', }) .toFormat(format, { quality, effort: 4, // баланс скорость/размер }) .withMetadata({ orientation: undefined }) // убрать EXIF rotation .toBuffer(); } // Middleware для lazy оптимизации app.get('/images/:key', async (req, res) => { const { key } = req.params; const { w, q = '80', f = 'webp' } = req.query; // Проверить кэш const cacheKey = `${key}:${w}:${q}:${f}`; const cached = await s3.getObject({ Key: `optimized/${cacheKey}.${f}` }).catch(() => null); if (cached) { return res.type(`image/${f}`).send(await streamToBuffer(cached.Body)); } // Получить оригинал const original = await s3.getObject({ Key: `originals/${key}` }); const buffer = await streamToBuffer(original.Body); // Оптимизировать const optimized = await optimizeImage(buffer, { maxWidth: w ? parseInt(w as string) : 1920, quality: parseInt(q as string), format: f as 'webp' | 'avif' | 'jpeg', }); // Сохранить в кэш await s3.putObject({ Key: `optimized/${cacheKey}.${f}`, Body: optimized, ContentType: `image/${f}`, CacheControl: 'public, max-age=31536000', }); res.type(`image/${f}`).send(optimized); }); : הוא צריך להכיל מילת מפתח, למשל, "תמונת מוצר מותאמת," שגם עוזרת לקידום אורגני.
Nginx: המרת WebP תוך כדי תנועה
# Отдавать WebP если браузер поддерживает
map $http_accept $webp_suffix {
"~*webp" ".webp";
default "";
}
server {
location ~* \.(jpg|jpeg|png)$ {
add_header Vary Accept;
try_files $uri$webp_suffix $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
}
} תהליך העבודה שלנו
- ניתוח — ביקורת על התמונות הנוכחיות: גודל ממוצע, פורמטים, מטא-דאטה. זיהוי תחומי שיפור.
- עיצוב תהליך — בחירת אסטרטגיה: תוך כדי תנועה (Nginx + ImageMagick) או עיבוד מוקדם בעת העלאה. עיצוב מערכת מטמון.
- יישום — כתיבת קוד אופטימיזציה, הגדרת מנגנון העלאה, הוספת srcset לתמונות רספונסיביות.
- בדיקות — אימות איכות חזותית, מדדי Core Web Vitals, מהירות אספקה. שימוש ב-Lighthouse ו-PageSpeed Insights.
- פריסה — פריסה על השרת, שילוב CDN (Cloudflare, AWS CloudFront), הגדרת ניטור.
מה כלול
- שילוב תהליך אופטימיזציה (Sharp/Intervention) לתוך מערכת הקצה שלכם.
- יצירת גדלים מרובים (400, 800, 1200, 1920 פיקסלים) לתמונות רספונסיביות.
- המרה ל-WebP ו-AVIF עם איכות אופטימלית.
- הסרת מטא-דאטה (EXIF) לחיסכון נוסף.
- מטמון CDN עם Cache-Control ארוך טווח.
- תיעוד לשימוש בעזרים (Blade, Twig, JSX).
- תמיכה לאחר היישום — 14 ימי ניטור.
רשימת בדיקה להערכת התהליך הנוכחי שלכם:
- שימוש בפורמטים מודרניים (WebP/AVIF);
- הסרת מטא-דאטה מיושמת;
- גדלים שונים נוצרים לרספונסיביות;
- מטמון CDN מוגדר;
- טעינה עצלה ופענוח אסינכרוני בשימוש.
לוחות זמנים לפרויקט
| רכיב | זמן משוער |
|---|---|
| שילוב Sharp בסיסי בעת העלאה | 2–3 ימים |
| המרה תוך כדי תנועה דרך Nginx | +1–2 ימים |
| תהליך מלא עם srcset רספונסיבי ו-CDN | 4–5 ימים |
התמחור מותאם אישית לפי הפרויקט שלכם — תלוי בערימה, במספר נקודות ההעלאה ובדרישות המטמון. עבור תעבורה ממוצעת, החיסכון יכול להיות משמעותי. צרו קשר להערכה ואנחנו נציע את הפתרון האופטימלי.
הזמינו פתרון אופטימיזציית תמונות בצד השרת במפתח מלא — האתר שלכם יהפוך למהיר משמעותית. עם ניסיון של למעלה מ-7 שנים, אופטימזנו תמונות ליותר מ-50 פרויקטים, מבלוגים קטנים ועד חנויות מסחר אלקטרוני עם תעבורה גבוהה. אנו מבטיחים הפחתה מינימלית של 40% בגודל התמונות תוך שמירה על איכות.







