Назад к блогу

Настройка прокси в Ruby через Net::HTTP и Faraday: полное руководство для Rails и standalone скриптов

Как правильно настроить прокси в Ruby-проектах через Net::HTTP и Faraday — с примерами для Rails и standalone скриптов, ротацией IP и обходом блокировок.

📅13 августа 2026 г.

Если вы пишете парсер, интегрируете внешний API или автоматизируете сбор данных на Ruby — рано или поздно вы столкнётесь с блокировками по IP. Wildberries, Ozon, Instagram и десятки других сервисов режут запросы с одного адреса уже после нескольких сотен обращений. Решение одно — прокси. В этой статье разберём, как подключить прокси к Ruby-проекту через стандартный Net::HTTP и популярный HTTP-клиент Faraday, настроить ротацию IP и корректно обработать ошибки — как в Rails-приложении, так и в простых скриптах.

Зачем Ruby-разработчику прокси: основные сценарии

Ruby активно используется для задач, где без прокси просто не обойтись. Прежде чем переходить к коду, стоит понять, в каких именно ситуациях они нужны — это поможет выбрать правильный тип и архитектуру решения.

Парсинг и сбор данных. Это самый распространённый сценарий. Вы пишете скрипт на Ruby, который обходит страницы Wildberries, Ozon или Авито, собирает цены и характеристики товаров. Без прокси такой скрипт заблокируют по IP уже через несколько минут. Сайты видят сотни запросов с одного адреса и автоматически включают защиту — капчу, временный бан или полный отказ в обслуживании.

Интеграция с гео-зависимыми API. Некоторые внешние сервисы возвращают разный контент в зависимости от страны запроса. Если ваше Rails-приложение обращается к такому API, прокси позволяет имитировать запросы из нужного региона. Например, проверять выдачу поисковиков для разных стран или получать цены, актуальные для конкретного рынка.

Тестирование и QA. Разработчики используют прокси для тестирования поведения приложения при запросах из разных IP-адресов и стран. Это особенно актуально для сервисов с гео-блокировкой или rate-limiting по IP.

Автоматизация маркетинга. Ruby-скрипты нередко используются для мониторинга рекламных кампаний, проверки позиций в выдаче или отслеживания активности конкурентов. Здесь прокси нужны для распределения нагрузки и обхода ограничений.

Работа с несколькими аккаунтами. Если ваш скрипт управляет несколькими аккаунтами на одной платформе, каждый аккаунт должен работать через отдельный IP — иначе платформа легко обнаружит связь и заблокирует всё разом.

Какой тип прокси выбрать для Ruby-задач

Не все прокси одинаково хорошо подходят для разных задач. Выбор типа напрямую влияет на успешность работы вашего скрипта. Разберём основные варианты применительно к Ruby-разработке.

Тип прокси Лучшие задачи в Ruby Скорость Обход защит
Прокси дата-центров Парсинг без строгой защиты, API-запросы, тестирование Высокая Средний
Резидентные прокси Парсинг защищённых сайтов, работа с соцсетями, маркетплейсы Средняя Высокий
Мобильные прокси Facebook, Instagram, TikTok API, работа с аккаунтами Средняя Максимальный

Прокси дата-центров — самый быстрый и дешёвый вариант. Подходят для парсинга сайтов без агрессивной защиты, массовых API-запросов и нагрузочного тестирования. Однако крупные платформы (Facebook, Google, Cloudflare) легко их распознают по ASN дата-центра.

Резидентные прокси используют IP реальных домашних пользователей. Для Ruby-скриптов, которые парсят Wildberries, Ozon или работают с защищёнными сайтами — это оптимальный выбор. Сайты видят обычного пользователя, а не сервер.

Мобильные прокси работают через IP мобильных операторов (4G/5G). Это наиболее трастовые адреса с точки зрения платформ — один мобильный IP могут использовать сотни реальных пользователей одновременно, поэтому блокировки по нему крайне редки. Если ваш Ruby-скрипт работает с социальными сетями или рекламными платформами — мобильные прокси снизят риск блокировок до минимума.

Прокси в Net::HTTP: базовая настройка

Net::HTTP — стандартная библиотека Ruby для HTTP-запросов, входящая в стандартную поставку языка. Она поддерживает прокси «из коробки» через специальный метод Net::HTTP::Proxy.

Самый простой способ — создать прокси-класс и использовать его вместо обычного Net::HTTP:

require 'net/http'
require 'uri'

# Данные прокси
proxy_host = 'proxy.example.com'
proxy_port = 8080

# Создаём прокси-класс
proxy_class = Net::HTTP::Proxy(proxy_host, proxy_port)

# Выполняем запрос через прокси
uri = URI('https://httpbin.org/ip')

proxy_class.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
  request = Net::HTTP::Get.new(uri)
  response = http.request(request)
  puts response.body
end

Метод Net::HTTP::Proxy принимает четыре параметра: хост прокси, порт, имя пользователя и пароль. Первые два обязательны, последние два — опциональны для прокси без аутентификации.

Альтернативный способ — передать параметры прокси напрямую в метод Net::HTTP.new:

require 'net/http'

uri = URI('https://httpbin.org/ip')

http = Net::HTTP.new(
  uri.host,
  uri.port,
  'proxy.example.com',  # proxy_addr
  8080,                  # proxy_port
  nil,                   # proxy_user (nil если без аутентификации)
  nil                    # proxy_pass
)

http.use_ssl = true

response = http.get(uri.path)
puts response.body

Этот подход удобен, когда вам нужно переиспользовать один и тот же HTTP-объект для нескольких запросов к одному хосту. Обратите внимание: параметры прокси передаются третьим и четвёртым аргументом, а не через отдельный метод.

Аутентификация и HTTPS через Net::HTTP

Большинство коммерческих прокси требуют аутентификации — логина и пароля. Кроме того, при работе с HTTPS-сайтами нужна дополнительная настройка SSL. Разберём оба случая.

Прокси с логином и паролем:

require 'net/http'
require 'uri'

proxy_host = 'proxy.example.com'
proxy_port = 8080
proxy_user = 'your_username'
proxy_pass = 'your_password'

# Создаём прокси-класс с аутентификацией
proxy_class = Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass)

uri = URI('https://httpbin.org/ip')

proxy_class.start(uri.host, uri.port, use_ssl: true) do |http|
  # Отключаем верификацию SSL (только для тестирования!)
  # http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  
  request = Net::HTTP::Get.new(uri)
  request['User-Agent'] = 'Mozilla/5.0 (compatible; MyBot/1.0)'
  
  response = http.request(request)
  puts "IP: #{response.body}"
  puts "Status: #{response.code}"
end

Важный момент: при использовании HTTPS-прокси (туннелирование через CONNECT) Net::HTTP автоматически устанавливает SSL-соединение с целевым сервером через прокси-туннель. Это работает корректно для большинства прокси-серверов.

Настройка через переменные окружения. Хорошая практика — не хардкодить данные прокси в коде, а читать их из переменных окружения:

require 'net/http'
require 'uri'

# Читаем настройки прокси из ENV
proxy_uri = URI(ENV.fetch('HTTP_PROXY', 'http://proxy.example.com:8080'))

proxy_class = Net::HTTP::Proxy(
  proxy_uri.host,
  proxy_uri.port,
  proxy_uri.user,
  proxy_uri.password
)

target_uri = URI('https://httpbin.org/ip')

proxy_class.start(target_uri.host, target_uri.port, use_ssl: true) do |http|
  response = http.get(target_uri.path)
  puts response.body
end

Переменную окружения можно задать в формате HTTP_PROXY=http://user:[email protected]:8080. Такой подход позволяет менять прокси без изменения кода — достаточно обновить переменную в .env файле или в настройках сервера.

Подключение прокси через Faraday

Faraday — один из самых популярных HTTP-клиентов в экосистеме Ruby. Он используется во многих Rails-приложениях благодаря удобному middleware-подходу и поддержке разных адаптеров (Net::HTTP, HTTParty, Typhoeus и другие). Настройка прокси в Faraday немного отличается в зависимости от используемого адаптера.

Базовая настройка прокси в Faraday:

require 'faraday'

# Создаём Faraday-соединение с прокси
conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
  faraday.proxy = {
    uri: 'http://proxy.example.com:8080',
    user: 'your_username',
    password: 'your_password'
  }
  
  faraday.headers['User-Agent'] = 'Mozilla/5.0 (compatible; MyBot/1.0)'
  faraday.adapter Faraday.default_adapter
end

response = conn.get('/ip')
puts response.body
puts "Status: #{response.status}"

Параметр faraday.proxy принимает хэш с ключами uri, user и password. Можно также передать строку URI напрямую, если прокси не требует аутентификации.

Faraday с адаптером Net::HTTP и расширенными настройками:

require 'faraday'

conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
  faraday.proxy = 'http://user:[email protected]:8080'
  
  # Настройки таймаутов
  faraday.options.timeout = 30
  faraday.options.open_timeout = 10
  
  # Middleware для логирования (удобно при отладке)
  faraday.response :logger
  
  # Retry при ошибках соединения
  faraday.request :retry, max: 3, interval: 1
  
  # Адаптер
  faraday.adapter :net_http
end

begin
  response = conn.get('/ip')
  puts response.body
rescue Faraday::ConnectionFailed => e
  puts "Ошибка соединения: #{e.message}"
rescue Faraday::TimeoutError => e
  puts "Таймаут: #{e.message}"
end

Middleware :retry особенно полезен при работе с прокси — если один адрес временно недоступен, Faraday автоматически повторит запрос. Это критично для надёжной работы скрипта в продакшне.

Faraday с адаптером Typhoeus (для параллельных запросов):

# Gemfile: gem 'typhoeus'
require 'faraday'
require 'typhoeus/adapters/faraday'

conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
  faraday.proxy = 'http://user:[email protected]:8080'
  faraday.adapter :typhoeus
end

# Параллельные запросы через прокси
responses = []
conn.in_parallel do
  responses << conn.get('/ip')
  responses << conn.get('/headers')
  responses << conn.get('/user-agent')
end

responses.each { |r| puts r.body }

Ротация прокси: автоматическая смена IP

Один прокси-адрес, даже резидентный, рано или поздно попадёт под ограничения при интенсивном парсинге. Решение — ротация: автоматическая смена IP после каждого запроса или через определённые интервалы. Реализуем простой ротатор для Ruby-скриптов.

Простой ротатор на основе массива прокси:

require 'net/http'
require 'uri'

class ProxyRotator
  def initialize(proxies)
    @proxies = proxies
    @index = 0
    @mutex = Mutex.new
  end

  def next_proxy
    @mutex.synchronize do
      proxy = @proxies[@index % @proxies.length]
      @index += 1
      proxy
    end
  end

  def random_proxy
    @proxies.sample
  end
end

# Список прокси в формате [host, port, user, pass]
proxies = [
  ['proxy1.example.com', 8080, 'user1', 'pass1'],
  ['proxy2.example.com', 8080, 'user2', 'pass2'],
  ['proxy3.example.com', 8080, 'user3', 'pass3'],
]

rotator = ProxyRotator.new(proxies)

# Парсим список URL через разные прокси
urls = [
  'https://httpbin.org/ip',
  'https://httpbin.org/headers',
  'https://httpbin.org/user-agent'
]

urls.each do |url|
  proxy = rotator.next_proxy
  proxy_class = Net::HTTP::Proxy(proxy[0], proxy[1], proxy[2], proxy[3])
  
  uri = URI(url)
  
  begin
    proxy_class.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
      http.read_timeout = 15
      response = http.get(uri.path)
      puts "#{url} -> Status: #{response.code}, Proxy: #{proxy[0]}"
    end
  rescue => e
    puts "Ошибка с прокси #{proxy[0]}: #{e.message}"
    # Переходим к следующему прокси автоматически
  end
  
  sleep(rand(0.5..2.0)) # Случайная задержка между запросами
end

Случайная задержка между запросами (sleep(rand(0.5..2.0))) — важный элемент. Паттерн запросов с фиксированными интервалами легко детектируется антиботовыми системами. Случайные задержки имитируют поведение реального пользователя.

Ротация через Faraday с middleware:

require 'faraday'

class ProxyMiddleware < Faraday::Middleware
  PROXIES = [
    'http://user1:[email protected]:8080',
    'http://user2:[email protected]:8080',
    'http://user3:[email protected]:8080',
  ].freeze

  def call(env)
    # Выбираем случайный прокси для каждого запроса
    proxy_uri = URI(PROXIES.sample)
    
    env[:request][:proxy] = {
      uri: proxy_uri,
      user: proxy_uri.user,
      password: proxy_uri.password
    }
    
    @app.call(env)
  end
end

# Регистрируем middleware
Faraday::Middleware.register_middleware proxy_rotator: ProxyMiddleware

conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
  faraday.use :proxy_rotator
  faraday.adapter :net_http
end

5.times do
  response = conn.get('/ip')
  puts response.body
end

Интеграция прокси в Rails-приложение

В Rails-приложении прокси чаще всего нужны для внешних HTTP-запросов: интеграции с API, фоновых задач парсинга или вебхуков. Рассмотрим несколько практических паттернов.

Паттерн 1: Сервисный объект с Faraday. Создаём базовый сервисный класс, который все внешние HTTP-запросы делает через прокси:

# app/services/http_client.rb
class HttpClient
  def self.connection(base_url)
    Faraday.new(url: base_url) do |faraday|
      # Прокси из переменных окружения Rails
      if Rails.env.production? && ENV['PROXY_URL'].present?
        faraday.proxy = ENV['PROXY_URL']
      end
      
      faraday.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
      faraday.options.timeout = 30
      faraday.options.open_timeout = 10
      
      faraday.request :retry, max: 3, interval: 2,
                      exceptions: [Faraday::ConnectionFailed, Faraday::TimeoutError]
      
      faraday.response :raise_error
      faraday.adapter :net_http
    end
  end
end

# Использование в контроллере или сервисе:
# client = HttpClient.connection('https://api.example.com')
# response = client.get('/products', { category: 'electronics' })

Паттерн 2: Настройка через Rails credentials. Для хранения данных прокси используйте зашифрованные credentials Rails:

# config/credentials.yml.enc (редактируется через rails credentials:edit)
# proxy:
#   host: proxy.example.com
#   port: 8080
#   username: your_user
#   password: your_pass

# app/services/proxy_service.rb
class ProxyService
  def self.faraday_proxy_config
    creds = Rails.application.credentials.proxy
    return nil unless creds
    
    {
      uri: "http://#{creds[:host]}:#{creds[:port]}",
      user: creds[:username],
      password: creds[:password]
    }
  end
  
  def self.net_http_proxy
    creds = Rails.application.credentials.proxy
    return Net::HTTP::Proxy(nil, nil) unless creds
    
    Net::HTTP::Proxy(
      creds[:host],
      creds[:port],
      creds[:username],
      creds[:password]
    )
  end
end

Паттерн 3: Фоновые задачи с Sidekiq. Если парсинг или API-запросы выполняются в фоновых воркерах, настройка прокси остаётся той же, но важно учитывать конкурентность:

# app/workers/price_parser_worker.rb
class PriceParserWorker
  include Sidekiq::Worker
  
  sidekiq_options retry: 3, queue: :parsers
  
  def perform(product_url)
    # Создаём новое соединение для каждого воркера
    proxy = ProxyService.net_http_proxy
    uri = URI(product_url)
    
    proxy.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
      http.read_timeout = 20
      
      request = Net::HTTP::Get.new(uri)
      request['User-Agent'] = random_user_agent
      
      response = http.request(request)
      
      if response.code == '200'
        parse_and_save(response.body, product_url)
      else
        Rails.logger.warn "Unexpected status #{response.code} for #{product_url}"
      end
    end
  rescue Net::OpenTimeout, Net::ReadTimeout => e
    Rails.logger.error "Timeout for #{product_url}: #{e.message}"
    raise # Sidekiq повторит задачу
  end
  
  private
  
  def random_user_agent
    agents = [
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
      'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
      'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
    ]
    agents.sample
  end
  
  def parse_and_save(html, url)
    # Логика парсинга...
  end
end

Обработка ошибок и таймауты

Работа через прокси добавляет дополнительный слой, который может генерировать специфические ошибки. Грамотная обработка исключений — это разница между скриптом, который упадёт через 5 минут, и скриптом, который работает часами без вмешательства.

Основные типы ошибок при работе с прокси в Ruby:

Исключение Причина Что делать
Net::OpenTimeout Прокси не отвечает Сменить прокси, повторить
Net::ReadTimeout Прокси медленный или сайт не отвечает Увеличить таймаут или сменить прокси
Errno::ECONNREFUSED Прокси-сервер отклонил соединение Проверить данные прокси
OpenSSL::SSL::SSLError Проблема с SSL через прокси Проверить настройки SSL
HTTP 407 Неверный логин/пароль прокси Проверить credentials
HTTP 403/429 IP заблокирован сайтом Сменить прокси, добавить паузу

Комплексная обработка ошибок с автоматической сменой прокси:

require 'net/http'
require 'uri'

class RobustHttpClient
  MAX_RETRIES = 3
  RETRIABLE_ERRORS = [
    Net::OpenTimeout,
    Net::ReadTimeout,
    Errno::ECONNREFUSED,
    Errno::ECONNRESET,
    OpenSSL::SSL::SSLError
  ].freeze

  def initialize(proxies)
    @proxies = proxies.dup
    @failed_proxies = []
  end

  def get(url, retries: MAX_RETRIES)
    uri = URI(url)
    attempt = 0

    begin
      attempt += 1
      proxy = current_proxy
      proxy_class = Net::HTTP::Proxy(*proxy)

      proxy_class.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
        http.open_timeout = 10
        http.read_timeout = 20

        request = Net::HTTP::Get.new(uri)
        request['User-Agent'] = 'Mozilla/5.0 (compatible; RubyBot/1.0)'

        response = http.request(request)

        case response.code.to_i
        when 200..299
          response
        when 407
          raise "Proxy authentication failed for #{proxy[0]}"
        when 403, 429
          rotate_proxy!
          raise "IP blocked (#{response.code}), rotating proxy"
        else
          raise "Unexpected HTTP status: #{response.code}"
        end
      end

    rescue *RETRIABLE_ERRORS => e
      Rails.logger.warn "Connection error (attempt #{attempt}/#{retries}): #{e.message}"
      rotate_proxy!
      retry if attempt < retries
      raise "Failed after #{retries} attempts: #{e.message}"

    rescue RuntimeError => e
      Rails.logger.warn "HTTP error (attempt #{attempt}/#{retries}): #{e.message}"
      retry if attempt < retries
      raise
    end
  end

  private

  def current_proxy
    @proxies.first || raise("No available proxies!")
  end

  def rotate_proxy!
    failed = @proxies.shift
    @failed_proxies << failed
    Rails.logger.info "Rotated proxy. Remaining: #{@proxies.length}"
  end
end

SOCKS5 прокси в Ruby: настройка через socksify

Стандартный Net::HTTP не поддерживает SOCKS5 нативно — только HTTP/HTTPS прокси. Для работы с SOCKS5 нужна дополнительная библиотека. Самый популярный вариант — гем socksify.

Почему SOCKS5 может быть предпочтительнее HTTP-прокси? Протокол SOCKS5 работает на более низком уровне — он проксирует любой TCP-трафик, а не только HTTP. Кроме того, SOCKS5 поддерживает аутентификацию и передаёт меньше метаданных о запросе, что делает его менее заметным для систем обнаружения ботов.

# Gemfile
# gem 'socksify'

require 'socksify'
require 'socksify/http'
require 'net/http'
require 'uri'

# Глобальная настройка SOCKS5 прокси
TCPSocket::socks_server = 'proxy.example.com'
TCPSocket::socks_port = 1080

# Опционально: аутентификация (SOCKS5 username/password)
# TCPSocket::socks_username = 'your_user'
# TCPSocket::socks_password = 'your_pass'

# Теперь все Net::HTTP запросы идут через SOCKS5
uri = URI('https://httpbin.org/ip')
response = Net::HTTP.get_response(uri)
puts response.body

Обратите внимание: при использовании socksify настройка применяется глобально ко всем TCP-соединениям в процессе. Если вам нужна более гибкая настройка — используйте Net::HTTP.SOCKSProxy из того же гема:

require 'socksify/http'

# Создаём SOCKS5 прокси-класс (аналогично Net::HTTP::Proxy для HTTP)
socks_proxy = Net::HTTP.SOCKSProxy('proxy.example.com', 1080)

uri = URI('https://httpbin.org/ip')

socks_proxy.start(uri.host, uri.port, use_ssl: true) do |http|
  response = http.get(uri.path)
  puts response.body
end

Для Faraday с SOCKS5 потребуется адаптер, поддерживающий этот протокол. Хорошим вариантом является faraday-net_http в связке с socksify, либо использование curl-based адаптера faraday-typhoeus, который нативно поддерживает SOCKS5 через libcurl.

💡 Совет по выбору протокола

Для большинства задач парсинга и работы с API достаточно HTTP/HTTPS прокси — они проще в настройке и поддерживаются нативно. SOCKS5 стоит использовать, когда вам нужно проксировать не только HTTP-трафик, или когда HTTP-прокси легко детектируются целевым сервисом. Мобильные и резидентные прокси доступны в обоих протоколах.

Заключение и рекомендации

Настройка прокси в Ruby не требует сложных библиотек — стандартный Net::HTTP поддерживает HTTP-прокси «из коробки», а Faraday делает интеграцию ещё удобнее благодаря middleware-архитектуре. Ключевые выводы из этого руководства:

  • Для Net::HTTP используйте Net::HTTP::Proxy — это встроенный и надёжный способ.
  • Для Faraday параметр faraday.proxy принимает URI строку или хэш с credentials.
  • Храните данные прокси в переменных окружения или Rails credentials — не хардкодьте в коде.
  • Реализуйте ротацию прокси и обработку ошибок для надёжной работы в продакшне.
  • Добавляйте случайные задержки и ротацию User-Agent для имитации человеческого поведения.
  • Для SOCKS5 используйте гем socksify.

Выбор типа прокси зависит от задачи. Если вы пишете парсер для Wildberries или Ozon — резидентные прокси дадут наилучший баланс между скоростью и обходом защит: реальные IP домашних пользователей крайне редко попадают в блоклисты. Для высоконагруженных задач с большим объёмом запросов к менее защищённым источникам подойдут прокси дата-центров — они быстрее и дешевле в расчёте на запрос. Если же ваш Ruby-скрипт работает с социальными сетями или рекламными API — обратите внимание на мобильные прокси: их IP-адреса имеют максимальный уровень доверия на большинстве платформ.