نوع MIME: چرا فایل درست سرو نمی‌شود

اگر مرورگر فایل CSS را دانلود می‌کند یا تصویر را اجرا می‌کند، مشکل از نوع MIME است. اینجا می‌بینید کدام هدر اشتباه است و چطور درستش کنید.

۶ دقیقه به‌روزرسانی ۱ مهر ۱۴۰۵

فایل CSS را باز می‌کنید و مرورگر به‌جای رندر کردن، یک پنجره دانلود نشان می‌دهد. یا بدتر: فایل .svg را آپلود کرده‌اید و کروم آن را به‌عنوان HTML اجرا می‌کند. این دو تا نشانه یک چیزند: هدر Content-Type که سرور می‌فرستد با محتوای واقعی فایل نمی‌خواند. مشکل از کد شما نیست، از نوع MIME است.

نوع MIME دقیقاً چه چیزی را تعیین می‌کند

هر بار که مرورگر یک منبع می‌گیرد، سرور در پاسخ یک هدر می‌فرستد شبیه این:

Content-Type: text/css; charset=UTF-8

بخش اول نوع MIME است، بخش دوم پارامتر charset. مرورگر بر اساس همین رشته تصمیم می‌گیرد فایل را رندر کند، اجرا کند، یا دانلود. اگر هدر نباشد یا اشتباه باشد، مرورگر به رفتار پیش‌فرض برمی‌گردد و آن رفتار تقریباً همیشه چیزی نیست که شما می‌خواهید.

نوع MIME از دو بخش ساخته می‌شود: type/subtype. مثلاً text/html، image/webp، application/json. استاندارد مرجعش IANA است و لیست رسمی مرتب به‌روز می‌شود، ولی مرورگرها عملاً زیرمجموعه‌ای از آن را می‌شناسند.

نوع‌های رایجی که واقعاً به آن‌ها برمی‌خورید

پسوندنوع MIME درستاگر اشتباه باشد چه می‌شود
.csstext/cssدانلود به‌جای رندر؛ سایت بدون استایل
.jsapplication/javascriptخطای MIME در کنسول، اسکریپت اجرا نمی‌شود
.svgimage/svg+xmlاجرای SVG به‌عنوان HTML؛ ریسک XSS
.woff2font/woff2فونت لود نمی‌شود، متن با فونت پیش‌فرض می‌آید
.jsonapplication/jsonfetch در جاوااسکریپت خطا می‌دهد
.webpimage/webpتصویر نمایش داده نمی‌شود

خطای Refused to execute script because its MIME type ('text/html') is not executable در کنسول کروم یعنی سرور به‌جای فایل JS، یک صفحه HTML برگردانده. این تقریباً همیشه یعنی مسیر فایل ۴۰۴ بوده و سرور صفحه خطای HTML را با کد ۲۰۰ فرستاده. مشکل از نوع MIME نیست، از مسیر است.

افزودن نوع سفارشی در آپاچی

اگر پسوندی دارید که آپاچی نمی‌شناسد، دو راه دارید. اولی از داخل .htaccess:

AddType application/wasm .wasm
AddType font/woff2 .woff2
AddType image/avif .avif

این خطوط را در ریشه دامنه بگذارید. اگر فایل‌ها را از CDN یا زیرپوشه سرو می‌کنید، .htaccess باید در همان مسیری باشد که فایل از آن سرو می‌شود، نه فقط ریشه.

راه دوم، سطح سرور است. در mime.types آپاچی یا در بلاک http نگینکس:

types {
    application/wasm wasm;
    font/woff2 woff2;
}

تفاوت مهم: .htaccess را بدون ری‌استارت سرور اعمال می‌کنید و روی هاست اشتراکی هم کار می‌کند. تغییر mime.types نیاز به دسترسی روت و ری‌استارت دارد، ولی روی همه دامنه‌های سرور اثر می‌گذارد. اگر فقط یک سایت دارید، .htaccess کافی است.

این‌جا اشتباه می‌کنند

رایج‌ترین اشتباهی که می‌بینم این است: کسی فونت .woff2 را آپلود می‌کند، فونت لود نمی‌شود، و می‌رود سراغ CSS و @font-face و ترتیب لود. در حالی که در تب Network مرورگر، پاسخ فونت کد ۲۰۰ است و هدرش Content-Type: application/octet-stream. مرورگر فونت را نمی‌شناسد و بی‌صدا ردش می‌کند. هیچ خطایی در کنسول نمی‌بینید. فقط متن با فونت پیش‌فرض می‌آید و شما ساعت‌ها دنبال باگ CSS می‌گردید.

نشانه‌اش این است: در تب Network، ستون Type را نگاه کنید. اگر برای فونت octet-stream نوشته، مشکل از نوع MIME است، نه از CSS.

نوع اشتباه چطور دانلود ناخواسته می‌سازد

وقتی سرور application/octet-stream می‌فرستد، مرورگر هیچ راهی برای رندر ندارد و فایل را دانلود می‌کند. این رفتار برای فایل‌های ناشناخته امن است، ولی برای CSS و JS و فونت فاجعه است.

حالت خطرناک‌ترش برعکس است: فایلی که باید دانلود شود، به‌عنوان HTML سرو شود. اگر فایل آپلودی کاربر را با Content-Type: text/html برگردانید، مرورگر آن را در دامنه شما اجرا می‌کند. این یعنی XSS ذخیره‌شده. برای همین در پیکربندی درست، پوشه آپلود باید یا نوع MIME محدود داشته باشد یا با Content-Disposition: attachment سرو شود.

چطور تشخیص بدهید کدام لایه نوع را تعیین می‌کند

اول با curl هدر واقعی را ببینید، نه آنچه مرورگر با کش نشان می‌دهد:

curl -I https://example.com/assets/style.css

خروجی Content-Type را نشان می‌دهد. اگر درست بود ولی مرورگر هنوز مشکل دارد، کش است. اگر غلط بود، بروید سراغ لایه‌ای که تعیینش می‌کند:

  1. آپاچی یا نگینکس: .htaccess و mime.types
  2. PHP: تابع header() در اسکریپتی که فایل را سرو می‌کند
  3. CDN: بعضی CDNها نوع را از مبدأ کپی می‌کنند، بعضی خودشان تعیین می‌کنند
  4. مرورگر: پسوند فایل روی سیستم شما، نه روی سرور

اگر فایل را با PHP سرو می‌کنید و header('Content-Type: ...') را فراموش کرده‌اید، PHP پیش‌فرض text/html می‌فرستد. این یکی از پرتکرارترین دلایل دانلود شدن فایل‌های PDF و تصویر در اسکریپت‌های آپلود دست‌ساز است.

نوع MIME و کش مرورگر

یک نکته که کمتر گفته می‌شود: مرورگر نوع MIME را در کش هم ذخیره می‌کند. اگر هدر را اصلاح کردید و کاربر هنوز فایل قدیمی را با نوع غلط می‌گیرد، مشکل از کش است. برای فایل‌های استاتیک، نسخه‌بندی در نام فایل (style.a3f9.css) این مشکل را کامل حل می‌کند و نیازی به پاک کردن کش کاربر نیست.

هدر X-Content-Type-Options: nosniff را هم جدی بگیرید. این هدر به مرورگر می‌گوید به نوع اعلام‌شده اعتماد کند و خودش حدس نزند. بدون آن، بعضی مرورگرها محتوای یک فایل را می‌خوانند و نوع را از محتوا حدس می‌زنند که نتیجه‌اش غیرقابل پیش‌بینی است.

کجا این تنظیمات را اعمال کنید

روی هاست اشتراکی، دسترسی شما به mime.types سرور بسته است و تنها ابزارتان .htaccess است. اگر سایت وردپرسی دارید و افزونه‌ای فایل‌های .webp یا .avif تولید می‌کند، اول مطمئن شوید نوع MIME آن‌ها درست اعلام می‌شود، بعد سراغ بهینه‌سازی بروید. برای بررسی سریع هدرها و رکوردهای DNS از ابزارهای رایگان وب‌مستر استفاده کنید و اگر لازم شد وضعیت دامنه را با بررسی DNS و شبکه چک کنید.

اگر تعداد فایل‌های استاتیک و دامنه‌ها زیاد شده و مدام باید .htaccess را دست‌کاری کنید، وقتش رسیده که کنترل کامل روی پیکربندی وب‌سرور داشته باشید. سرور اختصاصی این امکان را می‌دهد که mime.types را یک‌بار درست تنظیم کنید و روی همه سایت‌ها اعمال شود. برای سایت‌های کوچک‌تر، هاست لینوکس با دسترسی به .htaccess برای اکثر این موارد کافی است.

قبل از هر تغییر، از فایل پیکربندی نسخه پشتیبان بگیرید. یک خط اشتباه در .htaccess کل سایت را با خطای 500 از دسترس خارج می‌کند و اگر نسخه قبلی را نداشته باشید، باید از صفر بنویسیدش.

پرسش‌های پرتکرار

چرا فایل CSS من دانلود می‌شود و در مرورگر باز نمی‌شود؟

چون سرور برای آن نوع MIME اشتباه می‌فرستد، معمولاً application/octet-stream یا text/plain. مرورگر نمی‌داند این فایل باید رندر شود و آن را دانلود می‌کند. با curl -I هدر Content-Type را ببینید و اگر غلط بود، در .htaccess خط AddType text/css .css را اضافه کنید.

تفاوت نوع MIME و پسوند فایل چیست؟

پسوند فایل فقط بخشی از نام فایل است و سرور می‌تواند آن را نادیده بگیرد. نوع MIME چیزی است که سرور در هدر Content-Type اعلام می‌کند و مرورگر بر اساس آن تصمیم می‌گیرد. فایلی با پسوند .jpg می‌تواند نوع MIME text/html داشته باشد و مرورگر آن را به‌عنوان HTML اجرا کند.

چطور نوع MIME یک فایل را روی سرور چک کنم؟

ساده‌ترین راه curl -I https://example.com/file.ext است که هدرهای پاسخ را نشان می‌دهد. در مرورگر هم تب Network و ستون Type همین اطلاعات را می‌دهد، ولی اگر کش فعال باشد ممکن است مقدار قدیمی را ببینید. برای اطمینان، با پارامتر تصادفی مثل ?v=123 درخواست بزنید تا کش دور زده شود.

آیا نوع MIME روی سئو اثر دارد؟

مستقیم نه، ولی غیرمستقیم بله. اگر فایل CSS یا JS با نوع اشتباه سرو شود، صفحه ناقص رندر می‌شود و Core Web Vitals خراب می‌شود. اگر تصویری با نوع اشتباه سرو شود، گوگل ممکن است آن را ایندکس نکند و در نتایج تصویر ظاهر نشود. این‌ها روی رتبه اثر می‌گذارند، هرچند خود هدر Content-Type یک فاکتور رتبه‌بندی نیست.

آیا این مطلب برایتان مفید بود؟