عرفان قلی زاده
عرفان قلی زاده
خواندن ۷ دقیقه·۲ سال پیش

بهینه سازی تصویر با resize و ارائه webp در nginx

وب سایت شما موقع لود عکس ها کنده؟
نمره سئو تصاویر سایت شما چند هست؟
چند درصد از تصاویر سایتتون بهیته هست؟ برای بهینه سازی تصاویر از پلاگین استفاده میکنید؟
با فرمت های مناسب برای تصاویر سایت که سئو فرندلی هم باشه آشنایی دارید؟
چه جوری میتونیم از فرمت عکس webp استفاده کنیم اما به فرانت سایت دست نزنیم؟
میخواید فرمت عکس webp رو ساپورت بکنید اما لینک های قدیمی خراب نشه و لینک های جدید هم براتون توی سئو مشکل ایجاد نکنه؟
خب پس حتما این مقاله رو بخونید!

من عرفان قلی زاده هستم و در حال حاضر DevOps/SRE در شرکت وندار هستم، با توجه به اینکه تو دنیای سرچ انجین های امروز مخصوصا گوگل، SEO نقش مهمی در وب سایت های ما داره، تصمیم گرفتم که برای بهینه سازی لود عکس ها و سئو تصاویر با استفاده از وب سرور nginx یه مطلب رو براتون شرح بدم که هم برای افرادی که با سرور کار میکنند هم افرادی که در زمینه سئو تمرکز دارند به شدت توصیه میشه وقت بزارن و مطاله کنن.

بهینه سازی عکس با انجین ایکس
بهینه سازی عکس با انجین ایکس


توی این مطلب من فرض میکنم که شما کار با nginx رو بلد هستید و دارید گوشه های تاریک nginx رو با من کشف میکنید. (راستی چند روز پیش cloudflare اعلام کرد که دیگه nginx برای زیر ساختش کافی نیست و برای خودش یه محصول جدید رو از صفر با زبون Rust نوشته - در ستایش nginx بود)


لینک پروژه توی گیت هاب هم ببینید، به خوبی داکیومنت شده و انتهای مقاله هست⭐

شروع این ماجرا زمانی بود که من در شرکتی کار میکردم که عکس‌های بسیار زیادی در هر صفحه داشتیم و نیاز به بهینه‌سازی عکس‌ها هم برای بهبود سرعت سایت و هم برای بالا بردن امتیاز سئو به شدت حس میشد. برای انجام این کار قبل از اینکه من به این تیم ملحق بشم از همچین تنظیماتی استفاده میشد:

map $arg_width $width { default '-'; &quot~[0-9]+&quot $arg_width; } map $arg_width $height { default '-'; &quot~[0-9]+&quot $arg_height; } server { listen 80; server_name _ ; location ~* \.(?:jpg|jpeg|png|webp)$ { root /usr/share/nginx/html; try_files $uri $uri/ =404; image_filter_jpeg_quality 95; image_filter_webp_quality 100; image_filter_interlace on; image_filter_buffer 10M; image_filter resize $width $height; image_filter crop $width $height; image_filter_transparency on; } }

خب این کانفیگ داره چیکار میکنه؟
ما اول داریم پسوند های عکس رو توی یه location بلاک میگیریم و بعدش با استفاده از آرگومان هایی که قبلا از query string دریافت کردیم عکس رو به صورت اتوماتیک توی وب سرور تغییر اندازه میدیم.

اینجوری هر زمان که درخواستی عادی ارسال بشه کاربر عکس رو به شکل معمولی میبینه اما وقتی که با استفاده از query string به همراه درخواستش width و height ارسال بکنه عکس مناسب خودش رو میبینه.

مزیت این کار چیه:
۱. نیاز نیست تعداد زیادی عکس با اندازه های مختلف تولید کنیم.
۲. نمره SEO ما افزایش پیدا میکنه چون عکس متناسب با اندازه صفحه دستگاه کاربر رو براش فرستادیم.
۳. سرعت سایت افزایش قابل ملاحظه ای پیدا میکنه.


خب تا اینجا که عالی بود ... اما یه روزی رسید که اعلام شد بهتره سایت ها از فرمت عکس webp برای تصاویرشون استفاده کنن، این فرمت توسط خود گوگل ارائه شده و با حجم خیلی کمتر (توی تست های من در نمونه های تعداد بالا حدود یک پنجم حجم - در تعداد زیادی از عکس ها حتی یک دهم حجم اصلی) همون کیفیت jpg رو به ما میده (حتما میدونید که در ۹۹ درصد مواقع توی وب نیاز به استفاده از png نیست!)

برای این کار روش های متفاوتی هست میتونیم مثل روشی که برای resize استفاده کردیم به اصطلاح on-the-fly عکس رو تبدیل کنیم اما من با این سیستم خیلی موافق نبودم و دوست داشتم که سیستم مقداری بهینه تر و هوشمند تر کار بکنه.

اما اجازه بدید شمارو با چالش اصلی این قسمت آشنا بکنم ...
هر دستگاهی از webp ساپورت نمیکنه!
برای دور زدن این محدودیت میشه از یکی از هدرهایی که توی درخواست ارسال میشه به اسم accept header استفاده کرد.

خب بریم تنظیمات رو ببینیم بعد راجبش حرف میزنم.

map $http_accept $webp_suffix { default &quot&quot &quot~*webp&quot &quot.webp&quot } ... try_files $uri$webp_suffix $uri =404; ...

توی بلاک map اول چک میکنیم توی آرایه ای که کاربر برای فرمت هایی که دستگاه ساپورت میکنه webp رو فرستاده یا نه، اگر موجود باشه مقدار webp_suffix رو پر میکنیم در غیر اینصورت این مقدار خالی میمونه.
با تغییر قسمت try_files نسبت به تیکه کد قبلی کاری میکنیم که در صورتی که کاربر ساپورت لازم رو داشت، عکس با فرمت webp براش ارسال بشه.

خب هوشمندی این کجا بود؟؟
۱. زمانی که شما پسوند webp رو موجود نداشته باشید کاربر بدون هیچ مشکلی پسوند اصلی رو دریافت میکنه.
۲. زمانی که پسوند webp موجود باشه و کاربر هم ساپورت بکنه بدون تغییر url و redirect کاربر محتوای webp براش ارسال میشه (با همون پسوند اصلی ولی content-type مشخص میکنه که webp هست).
۳. ابزارهای SEO چون این فرمت رو ساپورت میکنند متوجه این مساله میشن که شما عکس webp دارید.


تا اینجا مسائل مربوط به گذشته بودند اما امروز، امروزه و باید به چالش های جدید برسیم ...
از اونجایی که مسئله high-availability بسیار مهم هست، من چند سالی میشه که یک کلاستر object storage رو مثل ceph و minio به جای لوکال استوریج یا انواع استوریج های قدیمی پیشنهاد میکنم اما ...
من با minio راحت تر بودم و در مقایسه هایی که داشتم برای مصارفی که ما داریم بهتر هست اما تمام توضیحات و تیکه کدهایی که بالا بود زمانی کار میکرد که ما به عکس به شکل یک فایل لوکال دسترسی داشتیم ...

توی همچین شرایطی بود که من پیشنهاد دادم که برای یکی از جلسات coffee technology توی وندار من روی این موضوع کار بکنم و حتی بیشتر پیش برم و ببینم زیرساختی مثل کلودفلر و نسخه کپی ایرانیش ابرآروان به عنوان یک CDN چجوری اینکارو انجام میدن؟

اگر تا اینجا متوجه نشدید که این حالت با دسترسی لوکال چه فرقی داره باید بگم که وقتی شما از minio استفاده میکنید باید درخواست رو proxy pass بکنید و ارسال فایل توسط زیرساخت minio انجام میشه.

حتما تا اینجا مطلب رو با دقت خوندید، پس بخش resize و webp رو توی دو قسمت مجزا براتون توضیح نمیدم و مستقیم میرم سراغ کانفیگ نهایی:

upstream minio_servers { server minio:9000 max_fails=0; keepalive 128; } location ~* \.(?:jpg|jpeg|png|webp)$ { root /usr/share/nginx/html; try_files /dev/null @image_webp; } location @image_webp { proxy_pass http://minio_servers$uri$webp_suffix; image_filter_jpeg_quality 95; image_filter_webp_quality 100; image_filter_interlace on; image_filter_buffer 10M; image_filter resize $width $height; image_filter crop $width $height; image_filter_transparency on; error_page 404 415 = @image; } location @image { proxy_pass http://minio_servers$uri; image_filter_jpeg_quality 95; image_filter_webp_quality 100; image_filter_interlace on; image_filter_buffer 10M; image_filter resize $width $height; image_filter crop $width $height; image_filter_transparency on; }

توی تیکه کد بالا ابتدا سرورهای upstream رو تعریف کردیم که برای ما minio هست (و برای یک CDN provider این قسمت تبدیل میشه به سرور های اصلی استفاده کننده از سرویس).

بعد سعی کردیم که که تصاویر رو بفرستیم به بلاک @image_webp دقت کنید که قسمت /dev/null به خاطر اینکه nginx امکانش رو نداره که اولین آرگومان یه named location باشه ضروری هست و در تاثیر منفی هم در performance نداره.

در صورتی که مرورگر کاربر فرمت عکس webp رو ساپورت نکنه (با هدر accept) در همین قسمت براش عکس اصلی ارسال میشه، در صورت اینکه ساپورت بکنه هم عکس webp براش با content-type درستی توی همون url ارسال میشه.
فقط زمانی ما به بلاک @webp میریم که به هر دلیلی عکس موجود نباشه یا موقع تبدیل به خطا برخورد کنیم که در این حالت به عنوان fallback سراغ عکس اصلی میریم.

در نهایت ...

سعی کردم به زبان خیلی ساده یه راهکار اساسی و پیشرفته برای بهینه سازی تصاویر یک سایت ارائه بدم که هم برای همکارانم مفید و کارآمد باشه و هم برای بچه هایی که تو زمینه سئو تکنیکال دنبال تریک‌های حرفه ای هستن کارا باشه. فرمت webp در حال حاضر یکی از بهترین فرمت ها برای تصاویر یک سایت هست که علاوه بر گوگل بیشتر موتورهای جستجو هم ازش پشتیبانی میکنن تو این مقاله با هم یاد گرفتیم چه جوری فرمت یه عکس رو از سمت سرور به webp تبدیل کنیم بدون اینکه نه تغییری تو فرانت ایجاد بشه نه تعداد زیادی لینک به ازای هر تصویر تو سایت ایجاد بشه. منتظر نظراتتون هم هستم، راستی تو لینکدین هم میتونید با من در ارتباط باشید
امیدوارم از این مطلب لذت ببرید و استفاده بکنید.

بهتون گفتم که من از arch استفاده میکنم؟ ? شما از چی استفاده میکنید؟
لینکدین من: https://www.linkedin.com/in/erfan-gholizade
لینک گیت هاب پروژه کامل این بلاگ: https://github.com/erfantkerfan/cdn-nginx-image-optimization


عرفان قلی زادهnginxwebpseoسئو تصاویر
شاید از این پست‌ها خوشتان بیاید