چهارشنبه ۰۸ بهمن ۱۴۰۴

Wednesday, January 28, 2026 GMT +3:30

محدود کردن لینک دانلود مستقیم فایل ها با PHP و htaccess

php-htaccess-download

همان طور که می دانیم، لینک دانلود یک فایل در وب به طور کلی به دو دسته مستقیم و غیر مستقیم تقسیم می شود، منظور از لینک های مستقیم مواردی هستند که عموما به فرمت فایل مورد نظر (به فرض zip.) ختم شده و با کلیک بر روی آنها یا وارد کردن آدرس در مرورگر، بلافاصله برای دانلود ارائه می شوند، قاعدتا تعریف و استفاده از این نوع لینک ها خیلی سخت و پیچیده نیست، اما مشکل اکثر مدیران سایت های مختلف که عمدتا نیاز به محدود کردن دانلود فایل ها یا دانلود با پرداخت هزینه و... دارند، ایجاد و مدیریت نوع دوم از لینک ها یعنی لینک های دانلود غیر مستقیم است، در این حالت کاربر با وارد کردن آدرس لینک در مرورگر (در پس زمینه و در سرور) به صفحه دیگری ارجاع داده شده و به طور مثال ابتدا وضعیت عضویت یا پرداخت هزینه دانلود لینک بررسی و سپس مجوز دسترسی (یا عدم دسترسی) به او داده می شود، از این رو در این مطلب قصد داریم نحوه انجام این کار را با استفاده از PHP و htaccess آموزش دهیم، به این امید که مفید واقع شود.

دستور htaccess برای جلوگیری از دانلود مستقیم


اگر با فایل htaccess. آشنا باشید، می دانید که با استفاده از آن می توان تنظیمات متفاوتی در سرورهای لینوکس و ویندوز (در صورت پیکره بندی مناسب) اعمال کرد که یکی از این تنظیمات، بررسی لینک درخواستی و محدود کردن حالت دانلود مستقیم آن است، در این شیوه در مرحله اول دستوری در htaccess، با استفاده از الگوی عبارات با قاعده فرمت فایل درخواستی را بررسی کرده، اگر فرمت مورد نظر جزء مواردی باشد که نمی خواهیم به صورت مستقیم دریافت شوند، آن را به صورت پارامتر به یک فایل PHP ارسال می کنیم (این کار در سرور و بدون اطلاع کاربر انجام می شود)، به نمونه زیر توجه کنید.
RewriteEngine on
RewriteRule ^file/([^/]+)(\.zip|\.pdf|\.jpg)$ /limit-direct-access/file/download.php?file=$1$2 [NC,L]
در این مثال ساده، در ریشه یا همان root سایت دایرکتوری با نام فرضی limit-direct-access ساخته ایم که درون آن یک فایل htaccess. با دستورات بالا قرار دارد، همچنین یک فولدر با نام file که درون آن فایل download.php به همراه سایر فایل های آماده برای دانلود جای گرفته اند (با دقت به قسمت دوم دستور RewriteRule، درک ترتیب قرار گرفتن فولدرها ساده می شود)، لذا در حالت عادی کاربر برای دانلود فایل فرضی test.zip از دایرکتوری file باید نمونه لینک زیر را از سرور درخواست کند:
http://localhost/limit-direct-access/file/test.zip
اما با توجه به دستور RewriteRule نوشته شده، کاربر بدون اینکه متوجه شود، لینک زیر را از سرور درخواست خواهد کرد:
http://localhost/limit-direct-access/file/download.php?file=test.zip
در واقع لینک اصلی برای سرور مفهوم لینک Rewrite شده را دارد.

بررسی مجوزها و ارسال فایل برای دانلود با PHP


قسمت اول کار که تغییر مسیر لینک کاربر از حالت مستقیم به غیر مستقیم بود به درستی انجام شد! حال نوبت PHP است که کار بررسی مجوزهای ورود، پرداخت و... را انجام دهد و در صورتی که شرایط باب طبع ما باشد، فایل را به مرورگر جهت دانلود ارسال کند، به نمونه زیر توجه کنید.
<?php
@$file_name = $_GET['file'];
//echo $file_name;

//بررسی مجوزهای کاربر
//تعیین نحوه بررسی مجوزها با توجه به برنامه و هدف شما می تواند متفاوت باشد، به طور مثال ممکن است هنگامی که کاربر مبلغ لینک را پرداخت کرده و تراکنش موفقیت آمیز باشد، ضمن نگهداری اطلاعات در دیتابیس جهت استفاده های بعدی، یک سشن آی دی تنظیم کنید، اکنون در هنگام دانلود می توان آن سشن آی دی را بررسی کرد و اگر تنظیم شده باشد، اجازه دسترسی به کاربر داد، یا برای کاربران وارد شده به سایت از همین شیوه می توان استفاده نمود.
$user_access = TRUE;

//کاربر مجاز به دانلود است
if($user_access == TRUE){
    //تابع برای بدست آوردن پسوند فایل
    function getExtension($file){
        preg_match('/\.[^\.]+$/i', $file, $ext);
        return $ext[0];
    }
    
    //نوع فایل    
    $file_type = NULL;
    switch(getExtension($file_name)){
        case 'zip':
        $file_type = 'application/zip';
        break;
        case 'pdf':
        $file_type = 'application/pdf';
        break;
        case 'jpg':
        $file_type = 'image/jpeg';
        break;                
    }
    
    //ارسال فایل به مرورگر برای دانلود
    ob_start();
    header('Content-Description: File Transfer');
    //header('Content-Type: application/octet-stream');    
    header('Content-Type: '.$file_type);
    header('Content-Disposition: attachment; filename='.$file_name);
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: '.filesize("$file_name"));
    ob_clean();
    flush();
    readfile("$file_name");
    exit;
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>وبگو | محدود کردن دسترسی مستقیم به لینک دانلود</title>
<!-- https://webgoo.ir -->
</head>
<body>
<?php
//کاربر مجاز به دانلود نیست
if($user_access == FALSE){
    echo 'Error!';    
}
?>
</body>
</html>
توضیح:
- همان طور که گفتیم نام فایل به صورت یک پارامتر و از طریق متد GET به فایل PHP ارسال می شود، بنابراین در خط اول متغیر file_name را با این روش مقداردهی می کنیم (که برابر با مقادیر دریافتی از پارامتر file است).
- در گام بعدی نیاز به بررسی مجوز دانلود کاربر است، نحوه انجام این مرحله کاملا دلخواه بوده و بستگی به هدف شما دارد، آنچه برای ما اهمیت دارد، مقداردهی متغیر user_access به صورت TRUE یا FALSE است، به طور مثال می توانید برای کاربرانی که تراکنش آنها موفقیت آمیز باشد، یک سشن آی دی تنظیم کنید یا فیلدی را در دیتابیس به صورت حالت پرداخت تعریف کرده و با تطبیق اطلاعات (نام کاربری و کلمه عبور)، وضعیت مجوز دانلود کاربر را بررسی کنید، در هر صورت از هر روشی که استفاده کنید نهایتا باید به نتیجه TRUE یا FALSE ختم شود، لطفا به جهت مفصل بودن این مبحث، در این رابطه به آموزش های سشن و نحوه ایجاد سیستم ورود و خروج سایت مراجعه کنید.
- پس از تعیین حالت TRUE یا FALSE برای متغیر user_access، در صورتی که مقدار TRUE باشد، یعنی کاربر مجاز به دانلود بوده و لذا باید فایل را به مرورگر ارسال کنیم، بدین منظور هم می توانید از دستور زیر برای تعیین نوع MIME فایل استفاده کنید.
header('Content-Type: application/octet-stream');
این دستور به صورت کلی است و برای اکثر فایل ها کاربرد دارد، هم می توانید به صورت اختصاصی نوع MIME فایل ها را با تابع getExtension و دستور switch مشخص کنید (این کار در نمونه کد آموزش حاضر انجام شده است).
نکته: MIME یک استاندارد تعریف شده در وب برای تعیین فایل های مختلف از نظر نوع است که می توانید لیستی از آنها را در لینک زیر مشاهده کنید.
لیست فرمت ها و معادل آنها در استاندارد MIME
- دستورات header که در ادامه تنظیم شده اند برای ارسال صحیح فایل به مرورگر و رعایت استانداردهای HTTP لازم هستند.
- تابع ob_clean و flush برای مدیریت بهتر نقل و انتقال اطلاعات و پاک کردن موارد اضافی از سرور است (ضروری نیست).
- در نهایت نیز تابع readfile فایل مورد نظر را به مرورگر ارسال کرده و دستور exit باعث پایان اجرای کدها می شود.
نکته 1: در صورتی که مقدار متغیر user_access برابر FALSE باشد، بخش دوم دستورات اجرا شده و پیغام Error نمایش داده می شود.
نکته 2: دقت کنید که به دلیل استفاده شدن از توابع header در کد، نباید قبل از اجرای این توابع هیچ نوع خروجی به مرورگر ارسال شود، این خروجی می تواند شامل BOM یا Byte Order Mark نیز شود، لذا باید مطمئن شوید که فایل PHP شما بدون BOM است که بدین منظور می توانید از برنامه Adobe Dreamweaver یا ++Notepad استفاده کرده و BOM را با ذخیره مجدد فایل حذف کنید (در نرم افزار ++Notepad باید از منوی Encoding و امکان Convert to UTF-8 without BOM استفاده کنید).

دانلود نمونه فایل های آموزش


دانلود نمونه فایل های آموزش محدود کردن  لینک دانلود مستقیم فایل ها با PHP و htaccess
دسته بندی: آموزش کاربردی » PHP
related مطالب بیشتر:
نمایش آمار بازدیدها با PHP بدون استفاده از دیتابیس
ساخت فید خوان آر اس اس (RSS Feed Reader) با PHP
ارسال پارامتر به URL و دریافت مقادیر با PHP
نمایش قسمتی از متن و پاراگراف با PHP
نحوه نمایش متن و تصاویر اتفاقی در PHP و MySQL
دیدگاه
more ۱۳۵ دیدگاه برای این مطلب ارسال شده است.
more چینش دیدگاه ها به ترتیب از جدیدترین به قدیمی ترین است.
Farzad
۱۳:۳۳ ۱۳۹۹/۰۳/۳۰
سلام ، خسته نباشید
من فایل هایی که در هاست میزارم به این شکل هست:
file/folder1/test.zip
file/newfolder/test.zip
file/folder2/folder3/test.zip
یعنی مشخص نیست فایل ها تو چه پوشه ای هستند فقط این مشخص هست که همگی در پوشه file قرار دارن و داخل اون مشخص نیست چند تا فولدر باید باز بشه تا به فایل رسید!
میشه درباره این هم توضیح بفرمایید
در اینصورت باید شکل دستور RewriteRule را متناسب با تعدد دایرکتوری ها تغییر دهید، به فرض فایل download.php را در فولدر file بسازید و دستور htaccess را به صورت زیر بنویسید:
RewriteRule ^file/(.+)(\.zip|\.pdf|\.jpg)$ /limit-direct-access/file/download.php?file=$1$2 [NC,L]
نکته: تست نکرده ایم اما در تئوری با این روش قسمت بین فرمت فایل و دایرکتوری file در آدرس URL به عنوان پارامتر اول و فرمت به عنوان پارامتر دوم تنظیم می شود، لطفا بررسی کنید اگر خطایی دریافت کردید اطلاع دهید تا رفع شود.
علی
۰۰:۳۷ ۱۳۹۹/۰۳/۲۷
ممنون از پاسختون
اما روش بالا جهت دانلود فایل کاربرد داره . من میخواستم به صورت استریم ویدئو پخش بشه اما آدرس مستقیم ویدئو توی سورس صفحه نمایش داده نشه که کاربر بتونه مستقیم فایل رو دانلود کنه
هر فایلی که در مرورگر پخش شود نهایتا به نحوی قابل دانلود است و در محیط وب امنیت صد در صدی برای فایل های عمومی وجود ندارد، با این حال در مورد بحث ویدئو استریمینگ تا آنجا که اطلاع داریم با روش Chunk در PHP هم می توانید این کار را انجام دهید:
https://www.sitepoint.com/community/t/loading-html5-video-with-php-chunks-or-not/350957/3
هرچند بهترین حالت محافظت استفاده از سرور استریم (Streaming Server) و رمزنگاری (Encrypte) فایل ها است، متاسفانه تجربه کاربردی در این زمینه نداریم که دقیق راهنماییتان کنیم اما شاید مطالب زیر کمک کنند:
https://docuserve.com/blog-post/video-encryption-live-streaming/

https://docuserve.com/blog-post/video-encryption-how-to-secure-your-digital-content/
علی
۰۲:۰۸ ۱۳۹۹/۰۳/۱۸
متاسفانه این روش برای فایلهای ویدئویی که به صورت استریم در سایت پخش میشوند جواب نمیده . اگر محدودیت ایجاد کنیم توی سایت پخش هم نخواهد شد . روشی برای این مشکل وجود داره؟
تست نکرده ایم اما احتمالا برای اینگونه فایل ها باید از روش Chunk یا دانلود چندبخشی استفاده کنید، در این شیوه شبیه به امکان "ادامه دانلود" این قابلیت وجود دارد که بخش خاصی از فایل به مرورگر کاربر ارسال شود، برای آشنایی بیشتر کدهای این صفحه را بررسی کنید:
https://stackoverflow.com/questions/6914912/streaming-a-large-file-using-php
دقت کنیم که معیار این آموزش صرف نظر از شیوه دانلود فایل وجود امکان دسترسی برای دانلود است، به عبارتی ابتدا کاربر باید وارد سایت شده یا به فرض هزینه را پرداخت کرده و مجاز به دانلود باشد.
۲۱:۳۸ ۱۳۹۸/۱۱/۰۷
عالی بود
احمدی
۱۴:۵۲ ۱۳۹۸/۰۶/۱۰
سلام ببخشید مثال اول که نوشتید
بر اساس مسیر فایلی که من دارم به چه شکل میشه؟
http://localhost/up/one_info/personal
/one_info_personal_1425.png
در دیتابیس چنین مسیری ذخیره کردم
up/one_info/personal
/one_info_personal_1425.png
حالا من میخوام این مسیر نمایش نده
باید چیکار کنم ؟
باید فایل download.php را به دایرکتوری personal انتقال دهید و همچنین دستورات فایل htaccess را به صورت نمونه زیر ویرایش کنید:
RewriteEngine on
RewriteRule ^personal/([^/]+)(\.zip|\.pdf|\.jpg)$ /up/one_info/personal/download.php?file=$1$2 [NC,L]
برای عدم نمایش مسیر باید دستور htaccess مناسب بنویسید به فرض:
RewriteEngine on
RewriteRule ^download/([^/]+)(\.zip|\.pdf|\.jpg)$ /up/one_info/personal/download.php?file=$1$2 [NC,L]
البته در لوکال ممکن است فایل htaccess در فولدر www قابل اجرا نباشد و در فولدرهای زیرمجموعه آن باید این فایل قرار گیرد، به فرض با کد بالا آدرس لینک به این شکل می شود:
http://localhost/download
/one_info_personal_1425.png
و نیاز است که فایل htaccess در دایرکتوری download قرار گیرد اما در سرور آنلاین فایل htaccess می تواند در فولدر www باشد.
۰۳:۴۰ ۱۳۹۸/۰۲/۲۸
درود بر شما ، مطلب بسیار کاربردی است و مشکل من هم هست. من هاست دانلودی دارم که به هاست اصلی من متصل هست. دقیقا من هم نمیخوام کاربر بدون ثبت نام یا پرداخت به فایلهای دانلودی دسترسی داشته باشد. سوال من الان این هست که آیا کدهایی که در بالا ذکر شده رو باید در فایل htaccess کپی کنم ؟ یعنی ویراش کنم فایل htaccess رو و این کدها رو کپی کنم اگر محبت کنین و در ایمیلم پاسخ بدید سپاسگزارتون هستم.
کدهای این آموزش صرفا نمونه است و به صورت مستقیم کاربردی نیست! در واقع برای استفاده از این مطلب باید با نحوه نوشتن و مفهوم دستورات در htaccess که مبتنی بر عبارات باقاعده (Regular Expressions) هستند آشنا باشید، دستکاری اشتباه محتویات این فایل ممکن است باعث ایجاد اختلال در سایت یا ایجاد نتایج دور از انتظار گردد.
۱۴:۵۳ ۱۳۹۸/۰۲/۱۳
چطور میشه توی فایل download.php چک کرد که این ریکوئست از سمت سایت اومده یا مثلا خود کاربر لینک رو دستی وارد کرده ؟؟
سایت هم با وردپرس اومده بالا و فروشگاهش هم ووکامرس یا EDD هستش ..
روش صددرصدی و کاملی برای این مسئله وجود ندارد، اما با
$_SERVER['HTTP_REFERER']
در PHP می توانیم لینک هایی که از طریق کلیک در صفحات وب به سایت هدایت می شوند را شناسایی کنیم، اگر این دستور مقدار خالی را برگرداند معمولا یعنی کاربر لینک را به صورت مستقیم یا شیوه ای دیگر از سرور درخواست کرده است (به فرض از طریق کلیک بر روی لینک در برنامه های مدیریت ایمیل و...).
<?php
$ref = @$_SERVER['HTTP_REFERER'];
if(!empty($ref)){

} else{

}
?>
mina
۰۸:۳۴ ۱۳۹۸/۰۲/۰۳
سلام وقت بخیر
میشه کدهامو براتون ارسال کنم ببینید ایراد کارم کجاست؟
ممنون
برای ارسال کدها لطفا از آدرس ایمیل سایت (موجود در قسمت تماس با ما) استفاده و پاسخ را از همان طریق پیگیر باشید.
معین
۱۴:۳۸ ۱۳۹۷/۰۸/۲۳
سلام
مشکل من شبیه روش زیر هست اما متاسفانه چون قدم به قدم توضیح داده نشده. نمیدونم دقیقا باید چکار کنم
https://stackoverflow.com/questions/10964399/protect-downloads-on-remote-server
لطفا کمکم کنین
ممنون
بهتر بود با جزئیات بیشتر و دقیقتری مشکل و هدفتان را توضیح می دادید تا امکان راهنمایی بهتر میسر می شد، در هر حال راه حل پیشنهادی استفاده از سیستم token است، به این صورت که در سرور 1 لینک دانلود را با مقدار md5 شده مرکب از IP کاربر و یک عبارت دلخواه به عنوان Salt برای کاربر وارد شده به سایت ایجاد می کنیم، در سرور 2 قبل از اینکه فایل را برای دانلود ارسال کنیم بررسی می کنیم که پارامتر رمزی ارسال شده و برابر با md5 ترکیبی IP کاربر و Salt باشد، مثال ها و ساختار در همان صفحه آمده است.
۱۵:۵۷ ۱۳۹۷/۰۵/۱۷
مرسی دوست عزیز واقعا استفاده کردم خیلی وقت بود دنبال این مراحل بودم
۱۱:۰۹ ۱۳۹۷/۰۵/۰۱
کمک بزرگی کردید مرسی
۱۷:۱۶ ۱۳۹۷/۰۲/۱۸
با عرض سلام
آقا من هیچ چیز از اینا سر در نمیارم ولی میدونم که میتونه مشکل سایت منو حل کنه شما میتونید زحمتشو بکشید این کدها رو برای من جایگزین کنید؟
این کدها صرفا یک نمونه هستند و باید مطابق با هدف و شرایط سایتتان تطبیق داده شوند که این کار برنامه نویسی است، متاسفانه به دلیل کمبود فرصت این امکان برایمان مقدور نیست.
مهدی
۲۰:۴۳ ۱۳۹۶/۰۶/۱۰
درست شد مهندس جان
آدرس فایل رو درست نداده بودم و باعث ایجاد مشکل شد.
مهدی
۱۴:۴۳ ۱۳۹۶/۰۶/۱۰
سلام و درود
ممنون و سپاس فراوان بابت مطالب و مخصوصا کامنت ها که در تکمیل مباحث بسیار تاثیر گزار هستش یه سوال دارم که تو کامنت ها نبود وقتی که فایل های دانلودی (قصد دانلودشون داریم) در کنار فایل download.php نباشد و در یک مسیر دیگه باشه چه سناریویی لازم است؟ (در واقع هم دسترسی به فایل غیر مستقیم هستش و هم اجازه از فایل download.php گرفته میشه) من سناریوی زیر رو انجام دادم ولی تا حدودی نا موفق هستم
بخش از فایل htaccess
حذف شد
که این بخش درست کار میکنه و درخواست رو به فایل browse.php میفرسته.
بخشی از فایل browse.php
حذف شد
در این وضعیت مسیر فایل در متغیر file_path قرار داره و این متغیر رو به تابع Readfile دادم.
حذف شد
حالا مشکل اینجاست موقعی که رو لینک دانلود کلیک میکنم، پنجره save نشان داده میشه با اسم فایل ولی حجم فایل 0 کیلو هستش و .... اگر بخش header ها رو غیر فعال کنیم و متغیر file_path رو به
header('Location:'.$file_path);
بدیم فایل بدون هیچ مشکلی فایل نشان داده می شود منتها در این صورت تو آدرس بار مسیر فایل مشخص میشه و من دوست دارم که از این روش استفاده نشه یک ورنینگ هم در بخش گرفتن پسوند وجود داره به صورتی که بعد از سوییچ کیس من مقدار file_type را چاپ میکنم هیچی چاپ نمیشه و همون مقدار null داخلشه ولی مشکلی بوجود نمیاره.
حالا اگه در مورد مشکل اول راهنمایی کنی بسیار ممنون و سپاسگزار هستم
علیرضا
۱۴:۲۹ ۱۳۹۶/۰۲/۱۱
سلام. خسته نباشید.
برای استفاده این دستورات در افزونه edd چنانچه بخواهیم شرط رو طوری کنیم که "اگر پرداخت انجام شد، لینک را نمایش بده" چه تغییری در قطعه کد زیر باید اعمال کرد؟
$user_access = TRUE;
اینکه از چه افزونه ای استفاده می کنید به خودی خود مهم نیست، باید قابلیتی در افزونه وجود داشته باشد که بتوان پس از پرداخت موفقیت آمیز آن را بررسی و در صورت تائید به طور مثال یک متغیر را با مقدار true تنظیم نمود، قاعدتا اعمال این شیوه نیاز به بررسی ساختار افزونه و ویرایش سورس کدهای آن مطابق با نیازمان دارد که یک کار برنامه نویسی است.
more لطفا پیش از ارسال دیدگاه نکات زیر را مد نظر داشته باشید:
- به سوالات کلی، زمانبر، مبهم و مشکلاتی که تلاشی برای رفع آنها نکرده باشید پاسخ مختصر داده شده یا به بخش برنامه نویسی اختصاصی ارجاع داده می شوند.
- کدها و اسکریپت های طولانی را ترجیحا در یک صفحه وب آنلاین یا به صورت حساب موقت و آزمایشی قرار دهید تا امکان بررسی دقیق مشکل و خطایابی میسر باشد.
- تمام دیدگاه های ارسالی خوانده شده و برای هر کاربر مدت زمان لازم جهت پاسخگویی در نظر گرفته می شود، لطفا از طرح سوالات متعدد در بازه زمانی کوتاه خودداری کنید.



 refresh
10 × 10
5 × 2
20 × 20
=