Перейти к содержанию

Руководство · На русском

Прокси для npm: настройка и проверка

У npm CLI есть собственная конфигурация сети. Работающий Axios, fetch или curl не подтверждает маршрут загрузки пакетов. Сначала выполните отдельный registry ping с временными настройками, конечным ожиданием и проверкой TLS, не меняя рабочие npmrc и не устанавливая зависимости.

Каталог и инструкции — на русском. Страница отдельного товара, аккаунт, корзина, оплата и полные условия обслуживания пока доступны на английском.

Опубликовано:

Короткие ответы

Настраивает ли агент Node.js прокси для npm?

Нет. npm запускается отдельным клиентом и читает свои параметры. Агент вашего приложения, переменные процесса и конфигурация npm отвечают за разные запросы. Проверяйте именно процесс, в котором возник сбой.

Чем proxy отличается от registry?

proxy и https-proxy задают шлюз, registry — сервер пакетов. Токен registry подтверждает доступ к нему, а пароль прокси — соединение со шлюзом. Смена адреса registry не заменяет настройку прокси.

Устанавливает ли проверка пакеты?

Нет. Она вызывает npm ping для выбранного HTTPS-registry. Установка может дополнительно обращаться к tarball- и Git-узлам и запускать скрипты; успешный ping эти действия не проверяет.

Можно ли отключить strict-ssl ради проверки?

Не следует. Найдите ошибку имени или цепочки доверия. Для разрешённого внутреннего центра сертификации пример может использовать явно заданный CA-файл, сохраняя проверку сертификата.

Проверьте версии рабочего процесса

Версии Node.js и npmsh
node --version
npm --version

Сохранённый пример рассчитан на npm 10, macOS и Linux и использует встроенные модули Node.js. Документация npm 10 сейчас обозначена Legacy. Это версия рецепта, а не рекомендация по выбору нового runtime: для другого npm проверьте его справку и совместимость сценария. В Windows данный скрипт намеренно останавливается.

Конфигурация npm описывает отдельные proxy, https-proxy, registry, noproxy и ограничения запросов. Сначала зафиксируйте версию именно в worker или CI. Версия вашего интерактивного терминала не подтверждает окружение давно запущенной задачи.

Разделите конфигурацию и авторизацию

Что отвечает за каждый этап
ПараметрНазначение
proxy / https-proxyМаршрут запросов npm к registry через шлюз.
registryАдрес сервера пакетов, а не промежуточного шлюза.
Токен с областью registryДоступ к конкретному registry; пароль прокси его не заменяет.
noproxy / NO_PROXYИсключения прямого подключения для назначений.
Агент или dispatcher приложенияТранспорт приложения, не отдельного npm CLI.

У проекта, пользователя и глобальной конфигурации есть разные npmrc. Не выводите всю конфигурацию в поддержку: она может содержать токены и URL с паролем. Сначала установите источник нужного параметра. Данные входа registry должны быть ограничены его узлом и путём.

Подготовьте изолированный тест

Возьмите полный логин и пароль в Connection builder · English. Сохраните скрипт как npm-proxy-check.mjs и запустите node npm-proxy-check.mjs в терминале. Секреты запрашиваются без отображения. PROXY_SERVER при необходимости меняет только адрес разрешённого HTTP-шлюза без данных входа в URL.

По умолчанию используется публичный npm registry. NPM_CHECK_REGISTRY можно задать для разрешённого HTTPS-registry, но существующие токены намеренно не загружаются. Сервер, которому нужен токен, может отклонить проверку. Это не инструкция для готового авторизованного доступа к частному registry.

Проверка намеренно исключает унаследованные настройки npm и прямые исключения только в своём дочернем процессе. Выполняйте её там, где выбранный маршрут разрешён. Скрипт не изменяет рабочие файлы npmrc, оболочку или сетевую политику. NPM_CHECK_CA допустим только для доверенного и разрешённого PEM-файла.

Выполните один registry ping

npm-proxy-check.mjs — тело исходного примераjavascript
import { execFile } from 'node:child_process';
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { createInterface } from 'node:readline';
import { Writable } from 'node:stream';
import { promisify } from 'node:util';

const run = promisify(execFile);

async function secret(name, label) {
  if (process.env[name]) return process.env[name];
  if (!process.stdin.isTTY) throw new Error('Secret input requires a terminal');
  process.stderr.write(label + ': ');
  const muted = new Writable({ write(_data, _encoding, done) { done(); } });
  const rl = createInterface({ input: process.stdin, output: muted, terminal: true });
  try {
    return await new Promise((resolve, reject) => {
      rl.once('SIGINT', () => reject(new Error('Input cancelled')));
      rl.once('close', () => reject(new Error('Input closed')));
      rl.question('', resolve);
    });
  } finally {
    rl.close();
    process.stderr.write('\n');
  }
}

async function main() {
  if (process.platform === 'win32') throw new Error('This example targets macOS and Linux');
  const proxy = new URL(process.env.PROXY_SERVER ?? 'http://gw.portproof.org:7000');
  const registry = new URL(process.env.NPM_CHECK_REGISTRY ?? 'https://registry.npmjs.org/');
  if (proxy.protocol !== 'http:' || proxy.username || proxy.password ||
      proxy.pathname !== '/' || proxy.search || proxy.hash) throw new Error('Invalid proxy');
  if (registry.protocol !== 'https:' || registry.username || registry.password ||
      registry.search || registry.hash) throw new Error('Invalid registry');
  const username = await secret('PROXY_USER', 'Full proxy username');
  const password = await secret('PROXY_PASSWORD', 'Proxy password');
  if (!username || !password || /[:\r\n\0]/.test(username) || /[\r\n\0]/.test(password)) {
    throw new Error('Invalid credentials');
  }
  proxy.username = encodeURIComponent(username);
  proxy.password = encodeURIComponent(password);

  // Keep npm's child environment small: no inherited npm settings, proxy
  // exceptions, registry tokens, NODE_OPTIONS or TLS-verification overrides.
  const childEnv = {};
  for (const name of ['PATH', 'HOME', 'TMPDIR', 'TMP', 'TEMP', 'LANG']) {
    if (process.env[name]) childEnv[name] = process.env[name];
  }
  // npm enables Node's compile cache outside its --cache directory by default.
  // Disable that optional cache for this one-off child so nothing is left behind.
  childEnv.NODE_DISABLE_COMPILE_CACHE = '1';
  const directory = await mkdtemp(join(tmpdir(), 'npm-proxy-check-'));
  const controller = new AbortController();
  const stop = () => controller.abort();
  process.once('SIGINT', stop);
  process.once('SIGTERM', stop);
  try {
    const config = [
      'proxy=' + JSON.stringify(proxy.href),
      'https-proxy=' + JSON.stringify(proxy.href),
      'noproxy=',
    ];
    // Optional certificate file for an approved internal registry.
    // This adds trust for this check; certificate verification remains on.
    if (process.env.NPM_CHECK_CA) {
      config.push('cafile=' + JSON.stringify(resolve(process.env.NPM_CHECK_CA)));
    }
    await writeFile(join(directory, 'user.npmrc'), config.join('\n') + '\n', { mode: 0o600 });
    await writeFile(join(directory, 'global.npmrc'), '', { mode: 0o600 });
    await run('npm', [
      'ping', '--json', '--registry=' + registry.href,
      '--userconfig=' + join(directory, 'user.npmrc'),
      '--globalconfig=' + join(directory, 'global.npmrc'),
      '--cache=' + join(directory, 'cache'),
      '--strict-ssl=true', '--fetch-retries=0', '--fetch-timeout=15000',
      '--logs-max=0', '--update-notifier=false', '--ignore-scripts=true',
    ], {
      cwd: directory, env: childEnv, signal: controller.signal,
      timeout: 20000, killSignal: 'SIGKILL', maxBuffer: 65536,
    });
    console.log('Registry ping succeeded. No package was installed.');
  } catch (error) {
    // Never echo npm's raw output: it may contain configuration details.
    let code = error.code === 'ABORT_ERR' ? 'ABORTED' : 'CHECK_FAILED';
    try {
      const reported = JSON.parse(error.stdout).error?.code;
      if (['E401', 'E403', 'E407', 'E502', 'ECONNREFUSED', 'ENOTFOUND',
        'ETIMEDOUT', 'ECONNRESET', 'FETCH_ERROR', 'CERT_HAS_EXPIRED',
        'SELF_SIGNED_CERT_IN_CHAIN', 'DEPTH_ZERO_SELF_SIGNED_CERT',
        'UNABLE_TO_VERIFY_LEAF_SIGNATURE'].includes(reported)) code = reported;
    } catch {}
    console.error('Registry ping failed: ' + code);
    process.exitCode = 1;
  } finally {
    process.removeListener('SIGINT', stop);
    process.removeListener('SIGTERM', stop);
    await rm(directory, { recursive: true, force: true });
  }
}

main().catch(() => {
  console.error('Check setup failed. Verify the gateway, HTTPS registry and secret input.');
  process.exitCode = 1;
});

Секреты кратковременно записываются в файл с правами 600 внутри собственного временного каталога. Скрипт ограничивает вывод, отключает лог-файлы npm и удаляет каталог при обычном завершении или обработанном прерывании. Авария машины или принудительное завершение могут оставить защищённые временные данные; права не защищают от администратора либо процесса вашего пользователя.

Автоматизация может передать PROXY_USER и PROXY_PASSWORD через свой механизм секретов. Не вставляйте реальные значения в команду оболочки или CI-лог. Эти переменные не передаются дочернему npm. Ни пароль шлюза, ни токен registry не нужны для публичного отчёта об ошибке.

Правильно истолкуйте результат

npm ping обращается к настроенному registry. Сценарий выдаёт только успех или ограниченную категорию ошибки. Он не печатает IP и не устанавливает географию. Успех относится к этому изолированному запуску, а не доказывает совпадение с рабочим проектом.

Для шлюза используйте отдельный curl-тест, затем сравните конфигурацию клиентов. Установка зависимости может загружать намного больше данных и обращаться к другим узлам. Не оценивайте её расход по одному ping и не запускайте install только ради диагностики proxy.

Найдите слой отказа перед изменением

Результат проверки npm
КатегорияСледующая проверка
E407 или отказ CONNECTШлюз, полный логин и пароль по диагностике 407.
E401 или E403 от registryРазрешения сервера пакетов; изолированный сценарий не читает сохранённый токен.
ECONNREFUSED или ENOTFOUNDАдрес, порт и среда worker, а не другого компьютера.
Таймаут или FETCH_ERRORДоступность шлюза и registry; повторы ограничены, сырой вывод намеренно скрыт.
Ошибка сертификатаДоверенная цепочка и имя назначения, без отключения strict-ssl.
Тест проходит, проект нетСравнить npmrc проекта, registry, окружение, исключения и дополнительные узлы загрузки.

Перенесите только разрешённые настройки

После диагностики используйте обычный механизм развёртывания и секретов проекта. Сам временный скрипт не чинит его конфигурацию. Сохраняйте область токенов registry, исключайте файлы с секретами из Git и ограничивайте повторы отказавших запросов. Для HTTP CONNECT есть отдельное руководство.

Измерьте разрешённый представительный запуск перед увеличением нагрузки, учитывая ошибки и повторные передачи. Тарифы Portproof описывают общий баланс мобильного и резидентского трафика; настройка npm не резервирует устройство. Покупка и кабинет остаются на английском. Справки проверены 31 октября 2026 года; живой registry, шлюз и реальная установка пакетов при подготовке статьи не тестировались.

Допустимое использование

Используйте прокси для разрешённых задач и соблюдайте правила принимающего сервиса. Настройка соединения не даёт права на доступ. Применяются правила допустимого использования · English.

Следующий шаг