
به اشتراک بگذارید

به اشتراک بگذارید
تحریم npm نامی است که توسعهدهندههای ایرانی روی تقریباً هر خطای نصب پکیج میگذارند. پیغام E403 در npm install یکی از آنهاست و Read timed out در pip یکی دیگر. راهحل پایدار در همه این ابزارها یکی است: نشانی مخزن را به یک میرور داخلی عوض کنید. برای روز قطعی هم یک کش محلی از پکیجها روی سرور نگه دارید.
| ابزار | تنظیمی که عوض میشود | فایل یا دستور اصلی | پشتیبان آفلاین |
|---|---|---|---|
| ابزار npm | کلید registry | فایل .npmrc یا npm config set | کش npm و Verdaccio |
| ابزار Yarn | کلید npmRegistryServer در نسخه 2 به بعد | فایل .yarnrc.yml | پوشه .yarn/cache |
| ابزار pip | کلید index-url | فایل pip.conf یا pip config | پوشه wheelhouse |
| ابزار Composer | مخزن packagist | فایل composer.json یا config -g | کش Composer و پوشه vendor |
💡 میرور پکیج یعنی نسخه همگام registry رسمی، داخل شبکه کشور
✅ قبل از عوض کردن هر تنظیمی، با یک دستور curl نوع خطا را مشخص کنید. خطای 403 و Timeout راهحل یکسانی ندارند.
✅ هر نشانی میرور را پیش از تنظیم سراسری، یک بار از روی همان سرور تست کنید.
خطای نصب پکیج معمولاً وسط کار سر میرسد: کد آماده است اما یک وابستگی کوچک دانلود نمیشود و pipeline پشتش میماند. اگر پروژه را روی یک سرور مجازی ایرانی اجرا میکنید، به احتمال زیاد همین الگو را دیدهاید. این راهنما به همان ترتیبی پیش میرود که مدیر سرور در عمل کار میکند. اول خطا را تشخیص میدهیم و بعد نشانی مخزن را در هر ابزار عوض میکنیم. آخر سر هم کشی میسازیم که نصبهای تیم را به اینترنت بینالملل وابسته نکند.
📌 آنچه در این مقاله میخوانید
◀️ علت خطای نصب پکیج روی سرور ایران چیست؟
◀️ تنظیم registry جایگزین برای npm و Yarn
◀️ تنظیم index-url در pip و میرور Packagist در Composer
◀️ ساخت کش محلی و نسخه آفلاین پکیجها برای تیم توسعه

پشت این خطاها سه علت متفاوت است: درخواستی که سرور مقصد با 403 رد میکند، مسیر بینالملل کند یا قطع که به Timeout میرسد، و مشکل گواهی SSL. هر کدام دستور تست و راهحل خودش را دارد.
بخش بزرگی از چیزی که تحریم npm خوانده میشود، شواهدی از مسدودسازی سمت سرویس ندارد. شرکت GitHub (مالک npm) در ژانویه ۲۰۲۱ اعلام کرد برای ارائه خدمات GitHub به توسعهدهندههای ایران از دولت آمریکا مجوز گرفته است؛ آن اعلامیه نامی از npm نمیبرد، پس تعمیمش به registry یک استنباط است. در گزارش ۲۰۱۹ کاربران ایرانی در مخزن pypi/warehouse هم خطای pypi.org به شکل Timeout در handshake TLS ثبت شد و در آن گفتوگو علت تحریمی تأیید نشد. برای registry رسمی npm، PyPI یا Packagist منبعی پیدا نکردیم که پاسخ 403 به IP ایران را ثبت کرده باشد؛ اگر خودتان چنین پاسخی دیدید، تست زیر آن را مشخص میکند.
| پیغام خطا | معنای محتمل | اقدام درست |
|---|---|---|
| کد E403 یا عبارت 403 Forbidden | سرور مقصد درخواست را رد کرده است؛ بهخاطر توکن، دسترسی پکیج یا سیاست سرویس | تست با curl بدون توکن، سپس تغییر registry |
| عبارت ETIMEDOUT یا Read timed out | پاسخی در زمان مجاز نرسیده؛ معمولاً مسیر بینالملل کند یا قطع است | تنظیم میرور داخلی |
| عبارت ECONNRESET یا handshake ناتمام | اتصال وسط راه قطع شده است | تست curl روی میرور داخلی؛ با کد 200 آن را تنظیم کنید |
| عبارت CERTIFICATE_VERIFY_FAILED یا UNABLE_TO_GET_ISSUER_CERT | گواهی میرور شناخته نمیشود یا ساعت سرور غلط است | اصلاح ساعت و معرفی CA (بخش عیبیابی پایینتر) |
| کد EINTEGRITY یا hashes do not match | فایل دریافتی با هش ثبتشده یکی نیست؛ کش خراب، فایل دستکاریشده یا tarball بازسازیشده | پاک کردن کش و نصب دوباره |
این سه دستور کد پاسخ HTTP و زمان کل را برای registry رسمی هر ابزار نشان میدهند. کد 200 با زمان کم یعنی مشکل در تنظیمات پروژه است، نه شبکه. کد 403 یعنی رد شدن درخواست و کد 000 همراه با خطای زمان یعنی مسیر باز نیست.
curl -sS -o /dev/null -m 20 -w '%{http_code} %{time_total}s\n' https://registry.npmjs.org/lodash
curl -sS -o /dev/null -m 20 -w '%{http_code} %{time_total}s\n' https://pypi.org/simple/requests/
curl -sS -o /dev/null -m 20 -w '%{http_code} %{time_total}s\n' https://repo.packagist.org/packages.json

ابزار npm همه پکیجها را از نشانی تنظیم registry میگیرد که مقدار پیشفرضش https://registry.npmjs.org/ است. چه علت خطا تحریم npm باشد چه اختلال مسیر، با عوض کردن همین نشانی، npm install و npm ci بدون تغییر در کد پروژه از میرور دانلود میکنند.
جدول زیر میرورهایی را نشان میدهد که سرویسدهندهشان نشانی و دستور تنظیم را در مستندات رسمی خود منتشر کرده است. این نشانیها از بیرون ایران قابل تست نبودند؛ پس قبل از استفاده، دستور تست هر ردیف را روی سرور خودتان اجرا کنید. فهرست کاملتر سرویسدهندهها در فهرست متنباز Mirava آمده است، اما نشانی دقیق را همیشه از سند رسمی خود سرویسدهنده بردارید.
| اکوسیستم | سرویسدهنده | سند رسمی |
|---|---|---|
| مخزن npm | لیارا | مستندات میرور npm لیارا |
| مخزن PyPI | لیارا | مستندات میرور PyPI لیارا |
| مخزن PyPI | رانفلر | مستندات میرور PyPI رانفلر |
| مخزن Composer | لیارا | مستندات میرور Composer لیارا |
نشانیهای منتشرشده در همین اسناد و دستور تست هر کدام اینهاست. کد 200 یعنی میرور از روی سرور شما پاسخ میدهد:
curl -sS -o /dev/null -m 20 -w '%{http_code}\n' https://package-mirror.liara.ir/repository/npm/lodash
curl -sS -o /dev/null -m 20 -w '%{http_code}\n' https://package-mirror.liara.ir/repository/pypi/simple/requests/
curl -sS -o /dev/null -m 20 -w '%{http_code}\n' https://mirror-pypi.runflare.com/simple/requests/
curl -sS -o /dev/null -m 20 -w '%{http_code}\n' https://package-mirror.liara.ir/repository/composer/packages.json
دستور زیر نشانی را در فایل ~/.npmrc کاربر ذخیره میکند. اگر فقط یک پروژه باید عوض شود، خط registry=... را در فایل .npmrc ریشه همان پروژه بنویسید تا همراه مخزن کد منتقل شود.
npm view lodash version --registry https://package-mirror.liara.ir/repository/npm/
npm config set registry https://package-mirror.liara.ir/repository/npm/
npm config get registry
# برگشت به حالت پیشفرض
npm config delete registry
فایل package-lock.json نشانی کامل هر tarball را در فیلد resolved نگه میدارد. طبق مستندات npm، مقدار پیشفرض replace-registry-host برابر npmjs است؛ یعنی فقط نشانیهایی که از registry رسمی آمدهاند با registry تنظیمشده جایگزین میشوند. اگر lockfile با میرور دیگری ساخته شده باشد، npm سراغ همان میرور قدیمی میرود. در این حالت خط replace-registry-host=always را در .npmrc همان پروژه بگذارید تا روی بقیه پروژهها اثر نکند.
در Yarn نسخه 2 به بعد، تنظیم معادل npmRegistryServer است؛ پیشفرض آن طبق مستندات Yarn نشانی https://registry.yarnpkg.com است. این دستور در فایل .yarnrc.yml پروژه مینویسد و گزینه -H آن را برای کل کاربر ثبت میکند. نسخه کلاسیک 1.22 کلید registry را میخواند.
# نسخه 2 به بعد
yarn config set npmRegistryServer https://package-mirror.liara.ir/repository/npm/
# نسخه کلاسیک 1.22
yarn config set registry https://package-mirror.liara.ir/repository/npm/
برای پکیجهای scope خصوصی، registry شرکت را جدا نگه دارید و فقط پکیجهای عمومی را به میرور بفرستید. در npm این کار با خطی مثل @mycorp:registry=... در .npmrc انجام میشود و در Yarn با کلید npmScopes.

ابزار pip پکیجها را از نشانی index-url میخواند و Composer از مخزنی به نام packagist. با عوض کردن همین دو مقدار، نصب پکیجهای پایتون و PHP هم مثل npm از میرور داخلی انجام میشود.
برای یک نصب تکی، گزینه -i کافی است. دستور pip config مقدار را در فایل کاربر مینویسد؛ طبق مستندات pip 26.2 این فایل در لینوکس $HOME/.config/pip/pip.conf است و هر virtualenv هم فایل $VIRTUAL_ENV/pip.conf خودش را دارد.
# یک بار
pip install -i https://package-mirror.liara.ir/repository/pypi/simple django
# دائمی برای کاربر فعلی
pip config --user set global.index-url https://package-mirror.liara.ir/repository/pypi/simple
pip config debug
# برگشت به PyPI رسمی
pip config --user unset global.index-url
متغیر محیطی PIP_INDEX_URL همین کار را بدون فایل روی دیسک میکند و برای Dockerfile و CI مناسبتر است. مراحل نصب خود Docker در آموزش نصب Docker روی اوبونتو 24.04 آمده است.
گزینه extra-index-url مخزن دوم را کنار مخزن اصلی میگذارد و pip از هر دو جستوجو میکند. مستندات pip هشدار میدهد که این کار برای پکیجهای خصوصی خطر dependency confusion دارد: پکیجی همنام در مخزن عمومی ممکن است بهجای پکیج شما انتخاب شود. در پروژهای که هم با تحریم npm و هم با pip درگیر است، میرور را جایگزین کنید.
تعریف pip برای trusted-host این است که میزبان «حتی بدون HTTPS معتبر» قابل اعتماد علامت میخورد. صفحه رانفلر این گزینه را هم در دستورهایش آورده است؛ اما وقتی میرور با HTTPS و گواهی معتبر پاسخ میدهد، این گزینه لازم نیست و بهتر است تنظیمش نکنید. در پروژههای حساس، requirements را با هش قفل کنید؛ pip فقط با گزینه --require-hashes یا وجود --hash در فایل، هش هر فایل را بررسی میکند.
در Composer نسخه 2.10، دستور زیر مخزن پیشفرض را با میرور عوض میکند. قالب این دستور و نشانی میرور از مستندات Composer لیارا آمده است؛ خود صفحه packagist.org/mirrors دستوری نمیدهد و کاربر را به سایت هر میرور ارجاع میدهد.
composer config -g repos.packagist composer https://package-mirror.liara.ir/repository/composer/
composer config --list --global
composer diagnose
# برگشت به Packagist رسمی
composer config -g --unset repos.packagist
صفحه رسمی Packagist میرورهای فهرستشدهاش را «میرور متادیتا» مینامد. در متادیتای رسمی، نشانی dist بسیاری از پکیجها به GitHub اشاره دارد؛ برای نمونه، هنگام نگارش این مقاله dist پکیج monolog/monolog در repo.packagist.org نشانی api.github.com را داشت. پس اگر میرور فقط متادیتا را برگرداند، فهرست نسخهها سریع میآید ولی فایل zip همچنان از GitHub دانلود میشود. با اجرای composer install -vvv نشانی هر دانلود در خروجی دیده میشود. اگر هنوز GitHub دیده شد، کش Composer روی سرور build (بخش بعد) این وابستگی را برای نصبهای بعدی برمیدارد.
اختلاف چند دقیقهای ساعت سرور کافی است تا گواهی معتبر هم رد شود. ساعت را بررسی کنید و همگامسازی NTP را روشن کنید؛ اگر سرور NTP خارجی در دسترس نیست، یک سرور NTP داخلی را در /etc/systemd/timesyncd.conf بگذارید.
timedatectl status
sudo timedatectl set-ntp true
اگر میرور گواهی صادرشده از یک CA داخلی دارد، فایل CA را به هر ابزار معرفی کنید. در Composer گزینه معادل cafile است. خاموش کردن strict-ssl فقط خطا را پنهان میکند و راه دریافت پکیج دستکاریشده را باز میگذارد.
npm config set cafile /etc/ssl/certs/internal-ca.pem
pip config --user set global.cert /etc/ssl/certs/internal-ca.pem
composer config -g cafile /etc/ssl/certs/internal-ca.pem
برای Timeout، پیشفرضها را بشناسید: طبق مستندات npm، fetch-timeout پنج دقیقه و fetch-retries دو است. در pip، --timeout پیشفرض ۱۵ ثانیه دارد و --retries حداکثر پنج تلاش برای برقراری اتصال است. بالا بردن این اعداد روی مسیر کند کمک میکند؛ روی مسیر قطع فقط انتظار را بیشتر میکند.

کش محلی یعنی هر پکیج فقط یک بار از بیرون دانلود شود و بعد از دیسک یک سرور داخل شبکه خودتان تحویل داده شود. برای تیمی که چند توسعهدهنده و یک CI دارد، این لایه قابلاتکاتر از هر میرور عمومی است، چون کنترلش دست خودتان است.
وقتی پنج نفر و یک CI هر روز npm ci میزنند، با کش فقط اولین درخواست بیرون میرود. چه مشکل از تحریم npm باشد چه از قطعی مسیر، نصبهای بعدی از دیسک داخلی انجام میشوند.
ابزار Verdaccio یک registry سبک npm است که درخواستها را به مخزن بالادستی (uplink) میفرستد و فایلها را در storage خودش نگه میدارد. طبق مستندات Verdaccio، نسخه 6 به Node.js 22 یا بالاتر نیاز دارد، بهطور پیشفرض فقط روی localhost:4873 گوش میدهد و گزینه cache آن بهطور پیشفرض روشن است.
npm install -g verdaccio
verdaccio # فایل config.yaml در اولین اجرا ساخته میشود
در فایل config.yaml دو تغییر لازم است: listen برای دسترسی بقیه سیستمها و نشانی uplink پیشفرض. نام npmjs را عوض نکنید؛ بخش packages در فایل پیشفرض به همین نام ارجاع میدهد.
listen: 0.0.0.0:4873
uplinks:
npmjs:
url: https://package-mirror.liara.ir/repository/npm/
timeout: 30s
maxage: 10m
پورت 4873 را فقط برای IPهای تیم و CI در فایروال باز کنید. روی سیستم هر توسعهدهنده هم نشانی سرور را registry بگذارید:
# نشانی سرور Verdaccio خودتان را بگذارید
npm config set registry http://10.0.0.5:4873/
رفتار Verdaccio هنگام قطع کامل uplink به maxage، fail_timeout و max_fails بستگی دارد؛ پیش از اتکا، یک بار uplink را عمداً قطع کنید و نصب را تست کنید.
دستور pip download همه وابستگیها را در یک پوشه میریزد و طبق مستندات pip، همان پوشه بعداً به --find-links داده میشود. با --no-index و یک پوشه محلی، pip سراغ هیچ مخزن شبکهای نمیرود.
pip download -r requirements.txt -d /srv/wheelhouse
pip install --no-index --find-links=/srv/wheelhouse -r requirements.txt
با هر تغییر requirements دستور اول را دوباره اجرا کنید. نسخه پایتون و معماری سرور مقصد باید با سرور دانلودکننده یکی باشد، وگرنه wheelهای باینری نصب نمیشوند.
ابزار Composer فایلهای dist را در پوشه کشی نگه میدارد که با متغیر COMPOSER_CACHE_DIR یا گزینه cache-dir جابهجا میشود. اگر این پوشه روی دیسک مشترک CI باشد، نصبهای بعدی از کش انجام میشوند. برای سرور تولید، مطمئنتر این است که پوشه vendor روی سرور build ساخته و همان خروجی منتقل شود.
export COMPOSER_CACHE_DIR=/srv/composer-cache
composer install --no-dev --prefer-dist --optimize-autoloader
کش مشترک به سروری نیاز دارد که هم توسعهدهندهها و هم CI به آن برسند. سرور مجازی ایران وبداده در دیتاسنتر تهران و روی شبکه رسپینا میزبانی میشود و IP مستقیمی دارد که از اینترنت بینالملل هم در دسترس است. پنل آن اسنپشات و بازگردانی دارد؛ پس قبل از ارتقای Verdaccio یا پاک کردن کش، یک اسنپشات میگیرید و اگر چیزی خراب شد برمیگردید. معیارهای انتخاب میزبانی داخل یا خارج در مقایسه سرور مجازی ایران و خارج آمده است.
پرسشهای رایج درباره خطای نصب پکیج روی سرورهای داخلی
سند رسمیای از npm درباره مسدودسازی ایران پیدا نکردیم. GitHub در ژانویه ۲۰۲۱ برای ارائه خدمات GitHub در ایران مجوز دولت آمریکا را گرفت، هرچند آن اعلامیه npm را نام نمیبرد. بیشتر گزارشها Timeout یا قطع اتصال است؛ اگر 403 بدون توکن دیدید، registry را عوض کنید.
اولی مخزن اصلی را جایگزین میکند و دومی مخزن دیگری کنار آن میگذارد. برای میرور، index-url را تنظیم کنید؛ extra-index-url در پروژههای دارای پکیج خصوصی خطر dependency confusion دارد.
در npm دستور npm config delete registry، در pip دستور pip config –user unset global.index-url و در Composer دستور composer config -g –unset repos.packagist. اگر تنظیم را در فایل پروژه نوشتهاید، همان خط را حذف کنید.
به نوع بررسی شما بستگی دارد. در npm، وقتی package-lock.json فیلد integrity دارد، فایل ناسازگار با EINTEGRITY رد میشود. pip فقط با –require-hashes هش را بررسی میکند و Composer برای zipballهای GitHub معمولاً هشی در lockfile ندارد. نصب اول بدون lockfile هم محافظتی ندارد؛ پس میرورهایی با HTTPS معتبر و سرویسدهنده شناختهشده انتخاب کنید.
چون نشانی فایل zip در متادیتای بسیاری از پکیجها به GitHub اشاره دارد و میرور متادیتا آن را عوض نمیکند. خروجی composer install -vvv را ببینید؛ کش Composer روی سرور build نصبهای بعدی را از این وابستگی جدا میکند.
بعضی پکیجها در مرحله postinstall فایل باینری را از GitHub یا سایت سازنده میگیرند و این دانلود از registry عبور نمیکند. نشانی دانلود را در خروجی خطا پیدا کنید؛ معمولاً README پکیج یک متغیر محیطی برای تغییر آن معرفی کرده است.
بله؛ میرور Docker Hub با کلید registry-mirrors در فایل daemon.json داکر تنظیم میشود و به npm و pip ربطی ندارد.
جمعبندی؛ تحریم npm را قبل از روز اختلال مدیریت کنید خطای نصب پکیج روی سرور داخلی سه علت جدا دارد و اولین کار، تشخیص با یک دستور curl است. پاسخ 403 یا Timeout یعنی باید نشانی مخزن را عوض کنید: registry در npm و Yarn، index-url در pip و repos.packagist در Composer. برای خطای گواهی، ساعت سرور و CA را درست کنید؛ خاموش کردن SSL راهحل نیست. بعد از تنظیم، lockfileهای ساختهشده با میرور دیگر و میرورهای Composer را که فقط متادیتا دارند بررسی کنید. برای تیم و CI هم یک کش داخلی بسازید؛ Verdaccio برای npm، پوشه wheelhouse برای pip و کش Composer یا پوشه vendor آماده. هر نشانی میرور را با دستور تست همین مقاله روی سرور خودتان امتحان کنید و دستورهای برگشت را کنارش یادداشت کنید تا در اختلال بعدی فقط یک دستور فاصله داشته باشید.
🚀 سرور لینوکس داخلی برای build و کش پکیج
سرور مجازی ایران وبداده بعد از پرداخت بهصورت خودکار تحویل میشود و قبل از خرید میتوانید آن را ساعتی و رایگان تست کنید تا تنظیمات میرور و کش را روی محیط واقعی بسنجید.
اگر به خطای خاصی برخوردید، متن آن را در بخش نظرات بنویسید.
منابع