Volver al blog

Configuración de proxy en Ruby con Net::HTTP y Faraday: guía completa para Rails y scripts independientes

Cómo configurar correctamente un proxy en proyectos Ruby a través de Net::HTTP y Faraday, con ejemplos para Rails y scripts independientes, rotación de IP y elusión de bloqueos.

📅13 de agosto de 2026
```html

Si estás escribiendo un parser, integrando una API externa o automatizando la recolección de datos en Ruby, tarde o temprano te encontrarás con bloqueos por IP. Wildberries, Ozon, Instagram y decenas de otros servicios limitan las solicitudes desde una sola dirección después de unas pocas centenas de accesos. La solución es una — proxies. En este artículo, analizaremos cómo conectar un proxy a un proyecto Ruby a través de la biblioteca estándar Net::HTTP y el popular cliente HTTP Faraday, configurar la rotación de IP y manejar errores correctamente — tanto en una aplicación Rails como en scripts simples.

¿Por qué necesita un proxy un desarrollador de Ruby: escenarios principales?

Ruby se utiliza activamente para tareas donde no se puede prescindir de proxies. Antes de pasar al código, es importante entender en qué situaciones son necesarios — esto ayudará a elegir el tipo y la arquitectura de la solución correcta.

Parsing y recolección de datos. Este es el escenario más común. Estás escribiendo un script en Ruby que navega por las páginas de Wildberries, Ozon o Avito, recolectando precios y características de productos. Sin un proxy, dicho script será bloqueado por IP en cuestión de minutos. Los sitios ven cientos de solicitudes desde una sola dirección y activan automáticamente la protección — captcha, baneo temporal o un rechazo total del servicio.

Integración con APIs geográficamente dependientes. Algunos servicios externos devuelven contenido diferente dependiendo del país de la solicitud. Si tu aplicación Rails se conecta a tal API, un proxy permite simular solicitudes desde la región deseada. Por ejemplo, verificar los resultados de los motores de búsqueda para diferentes países o recibir precios relevantes para un mercado específico.

Pruebas y QA. Los desarrolladores utilizan proxies para probar el comportamiento de la aplicación con solicitudes desde diferentes direcciones IP y países. Esto es especialmente relevante para servicios con geobloqueo o limitación de tasa por IP.

Automatización del marketing. Los scripts de Ruby se utilizan a menudo para monitorear campañas publicitarias, verificar posiciones en los resultados o rastrear la actividad de competidores. Aquí, los proxies son necesarios para distribuir la carga y eludir restricciones.

Trabajo con múltiples cuentas. Si tu script gestiona varias cuentas en una misma plataforma, cada cuenta debe operar a través de una IP separada — de lo contrario, la plataforma puede detectar fácilmente la conexión y bloquear todo de una vez.

Qué tipo de proxy elegir para tareas en Ruby

No todos los proxies son igualmente adecuados para diferentes tareas. La elección del tipo influye directamente en el éxito de tu script. Analicemos las opciones principales en relación con el desarrollo en Ruby.

Tipo de proxy Mejores tareas en Ruby Velocidad Elusión de protecciones
Proxies de centros de datos Parsing sin protección estricta, solicitudes API, pruebas Alta Media
Proxies residenciales Parsing de sitios protegidos, trabajo con redes sociales, marketplaces Media Alta
Proxies móviles Facebook, Instagram, TikTok API, trabajo con cuentas Media Máxima

Proxies de centros de datos — la opción más rápida y económica. Son adecuados para el parsing de sitios sin protección agresiva, solicitudes masivas de API y pruebas de carga. Sin embargo, plataformas grandes (Facebook, Google, Cloudflare) pueden reconocerlas fácilmente por el ASN del centro de datos.

Proxies residenciales utilizan IP de usuarios domésticos reales. Para scripts de Ruby que parsean Wildberries, Ozon o trabajan con sitios protegidos, esta es la opción óptima. Los sitios ven a un usuario normal, no a un servidor.

Proxies móviles funcionan a través de IP de operadores móviles (4G/5G). Estas son las direcciones más confiables desde el punto de vista de las plataformas — una IP móvil puede ser utilizada por cientos de usuarios reales al mismo tiempo, por lo que los bloqueos son extremadamente raros. Si tu script de Ruby trabaja con redes sociales o plataformas publicitarias, los proxies móviles reducirán el riesgo de bloqueos al mínimo.

Proxy en Net::HTTP: configuración básica

Net::HTTP — la biblioteca estándar de Ruby para solicitudes HTTP, incluida en la distribución estándar del lenguaje. Soporta proxies "out of the box" a través de un método especial Net::HTTP::Proxy.

La forma más sencilla es crear una clase de proxy y usarla en lugar de Net::HTTP:

require 'net/http'
require 'uri'

# Datos del proxy
proxy_host = 'proxy.example.com'
proxy_port = 8080

# Creamos la clase de proxy
proxy_class = Net::HTTP::Proxy(proxy_host, proxy_port)

# Ejecutamos la solicitud a través del proxy
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

El método Net::HTTP::Proxy acepta cuatro parámetros: el host del proxy, el puerto, el nombre de usuario y la contraseña. Los primeros dos son obligatorios, los últimos dos son opcionales para proxies sin autenticación.

Otra forma alternativa es pasar los parámetros del proxy directamente al método 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 si no hay autenticación)
  nil                    # proxy_pass
)

http.use_ssl = true

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

Este enfoque es conveniente cuando necesitas reutilizar el mismo objeto HTTP para múltiples solicitudes a un mismo host. Ten en cuenta que los parámetros del proxy se pasan como el tercer y cuarto argumento, no a través de un método separado.

Autenticación y HTTPS a través de Net::HTTP

La mayoría de los proxies comerciales requieren autenticación — nombre de usuario y contraseña. Además, al trabajar con sitios HTTPS, se necesita una configuración adicional de SSL. Analicemos ambos casos.

Proxy con nombre de usuario y contraseña:

require 'net/http'
require 'uri'

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

# Creamos la clase de proxy con autenticación
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|
  # Desactivamos la verificación de SSL (¡solo para pruebas!)
  # 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

Un punto importante: al usar proxies HTTPS (túnel a través de CONNECT) Net::HTTP establece automáticamente una conexión SSL con el servidor de destino a través del túnel proxy. Esto funciona correctamente para la mayoría de los servidores proxy.

Configuración a través de variables de entorno. Una buena práctica es no hardcodear los datos del proxy en el código, sino leerlos de las variables de entorno:

require 'net/http'
require 'uri'

# Leemos la configuración del proxy desde 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

La variable de entorno se puede establecer en el formato HTTP_PROXY=http://user:[email protected]:8080. Este enfoque permite cambiar el proxy sin modificar el código — solo es necesario actualizar la variable en el archivo .env o en la configuración del servidor.

Conexión de proxy a través de Faraday

Faraday es uno de los clientes HTTP más populares en el ecosistema Ruby. Se utiliza en muchas aplicaciones Rails gracias a su enfoque conveniente de middleware y soporte para diferentes adaptadores (Net::HTTP, HTTParty, Typhoeus y otros). La configuración de proxies en Faraday es un poco diferente dependiendo del adaptador utilizado.

Configuración básica de proxy en Faraday:

require 'faraday'

# Creamos una conexión Faraday con proxy
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}"

El parámetro faraday.proxy acepta un hash con las claves uri, user y password. También se puede pasar una cadena URI directamente, si el proxy no requiere autenticación.

Faraday con adaptador Net::HTTP y configuraciones avanzadas:

require 'faraday'

conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
  faraday.proxy = 'http://user:[email protected]:8080'
  
  # Configuración de timeouts
  faraday.options.timeout = 30
  faraday.options.open_timeout = 10
  
  # Middleware para logging (útil para depuración)
  faraday.response :logger
  
  # Retry en caso de errores de conexión
  faraday.request :retry, max: 3, interval: 1
  
  # Adaptador
  faraday.adapter :net_http
end

begin
  response = conn.get('/ip')
  puts response.body
rescue Faraday::ConnectionFailed => e
  puts "Error de conexión: #{e.message}"
rescue Faraday::TimeoutError => e
  puts "Timeout: #{e.message}"
end

El middleware :retry es especialmente útil al trabajar con proxies — si una dirección está temporalmente no disponible, Faraday repetirá automáticamente la solicitud. Esto es crítico para el funcionamiento confiable del script en producción.

Faraday con adaptador Typhoeus (para solicitudes paralelas):

# 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

# Solicitudes paralelas a través de proxy
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 }

Rotación de proxies: cambio automático de IP

Una sola dirección de proxy, incluso residencial, tarde o temprano caerá bajo restricciones durante un parsing intensivo. La solución es la rotación: cambio automático de IP después de cada solicitud o a intervalos determinados. Implementemos un simple rotador para scripts de Ruby.

Un simple rotador basado en un array de proxies:

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

# Lista de proxies en formato [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)

# Parseamos una lista de URL a través de diferentes proxies
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 "Error con el proxy #{proxy[0]}: #{e.message}"
    # Pasamos automáticamente al siguiente proxy
  end
  
  sleep(rand(0.5..2.0)) # Retraso aleatorio entre solicitudes
end

El retraso aleatorio entre solicitudes (sleep(rand(0.5..2.0))) es un elemento importante. El patrón de solicitudes con intervalos fijos es fácilmente detectado por sistemas anti-bot. Los retrasos aleatorios imitan el comportamiento de un usuario real.

Rotación a través de Faraday con 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)
    # Elegimos un proxy aleatorio para cada solicitud
    proxy_uri = URI(PROXIES.sample)
    
    env[:request][:proxy] = {
      uri: proxy_uri,
      user: proxy_uri.user,
      password: proxy_uri.password
    }
    
    @app.call(env)
  end
end

# Registramos el 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

Integración de proxies en una aplicación Rails

En una aplicación Rails, los proxies son más necesarios para solicitudes HTTP externas: integración con APIs, tareas en segundo plano de parsing o webhooks. Analicemos algunos patrones prácticos.

Patrón 1: Objeto de servicio con Faraday. Creamos una clase de servicio básica que realiza todas las solicitudes HTTP externas a través de un proxy:

# app/services/http_client.rb
class HttpClient
  def self.connection(base_url)
    Faraday.new(url: base_url) do |faraday|
      # Proxies desde variables de entorno de 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

# Uso en un controlador o servicio:
# client = HttpClient.connection('https://api.example.com')
# response = client.get('/products', { category: 'electronics' })

Patrón 2: Configuración a través de credenciales de Rails. Para almacenar datos del proxy, utiliza credenciales de Rails encriptadas:

# config/credentials.yml.enc (editado a través de 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

Patrón 3: Tareas en segundo plano con Sidekiq. Si el parsing o las solicitudes API se realizan en trabajadores en segundo plano, la configuración del proxy permanece igual, pero es importante considerar la concurrencia:

# app/workers/price_parser_worker.rb
class PriceParserWorker
  include Sidekiq::Worker
  
  sidekiq_options retry: 3, queue: :parsers
  
  def perform(product_url)
    # Creamos una nueva conexión para cada trabajador
    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 "Estado inesperado #{response.code} para #{product_url}"
      end
    end
  rescue Net::OpenTimeout, Net::ReadTimeout => e
    Rails.logger.error "Timeout para #{product_url}: #{e.message}"
    raise # Sidekiq repetirá la tarea
  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)
    # Lógica de parsing...
  end
end

Manejo de errores y timeouts

Trabajar a través de proxies añade una capa adicional que puede generar errores específicos. Un manejo adecuado de excepciones es la diferencia entre un script que se cae en 5 minutos y uno que funciona durante horas sin intervención.

Tipos principales de errores al trabajar con proxies en Ruby:

Excepción Causa Qué hacer
Net::OpenTimeout El proxy no responde Cambiar proxy, repetir
Net::ReadTimeout Proxy lento o el sitio no responde Aumentar timeout o cambiar proxy
Errno::ECONNREFUSED El servidor proxy rechazó la conexión Verificar datos del proxy
OpenSSL::SSL::SSLError Problema con SSL a través del proxy Verificar configuración de SSL
HTTP 407 Nombre de usuario/contraseña incorrectos del proxy Verificar credenciales
HTTP 403/429 IP bloqueada por el sitio Cambiar proxy, añadir pausa

Manejo integral de errores con cambio automático de proxy:

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 "La autenticación del proxy falló para #{proxy[0]}"
        when 403, 429
          rotate_proxy!
          raise "IP bloqueada (#{response.code}), rotando proxy"
        else
          raise "Estado HTTP inesperado: #{response.code}"
        end
      end

    rescue *RETRIABLE_ERRORS => e
      Rails.logger.warn "Error de conexión (intento #{attempt}/#{retries}): #{e.message}"
      rotate_proxy!
      retry if attempt < retries
      raise "Falló después de #{retries} intentos: #{e.message}"

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

  private

  def current_proxy
    @proxies.first || raise("¡No hay proxies disponibles!")
  end

  def rotate_proxy!
    failed = @proxies.shift
    @failed_proxies << failed
    Rails.logger.info "Proxy rotado. Restantes: #{@proxies.length}"
  end
end

Proxy SOCKS5 en Ruby: configuración a través de socksify

La Net::HTTP estándar no soporta SOCKS5 de forma nativa — solo proxies HTTP/HTTPS. Para trabajar con SOCKS5 se necesita una biblioteca adicional. La opción más popular es la gema socksify.

¿Por qué SOCKS5 puede ser preferible a un proxy HTTP? El protocolo SOCKS5 opera a un nivel más bajo — proxy cualquier tráfico TCP, no solo HTTP. Además, SOCKS5 soporta autenticación y transmite menos metadatos sobre la solicitud, lo que lo hace menos detectable para los sistemas de detección de bots.

# Gemfile
# gem 'socksify'

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

# Configuración global de proxy SOCKS5
TCPSocket::socks_server = 'proxy.example.com'
TCPSocket::socks_port = 1080

# Opcional: autenticación (SOCKS5 username/password)
# TCPSocket::socks_username = 'your_user'
# TCPSocket::socks_password = 'your_pass'

# Ahora todas las solicitudes Net::HTTP van a través de SOCKS5
uri = URI('https://httpbin.org/ip')
response = Net::HTTP.get_response(uri)
puts response.body

Ten en cuenta: al usar socksify, la configuración se aplica globalmente a todas las conexiones TCP en el proceso. Si necesitas una configuración más flexible, utiliza Net::HTTP.SOCKSProxy de la misma gema:

require 'socksify/http'

# Creamos una clase de proxy SOCKS5 (similar a Net::HTTP::Proxy para 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

Para Faraday con SOCKS5 se necesitará un adaptador que soporte este protocolo. Una buena opción es faraday-net_http en combinación con socksify, o el uso de un adaptador basado en curl faraday-typhoeus, que soporta nativamente SOCKS5 a través de libcurl.

💡 Consejo sobre la elección del protocolo

Para la mayoría de las tareas de parsing y trabajo con APIs, los proxies HTTP/HTTPS son suficientes — son más fáciles de configurar y se soportan de forma nativa. SOCKS5 debe utilizarse cuando necesitas proxy no solo tráfico HTTP, o cuando los proxies HTTP son fácilmente detectados por el servicio objetivo. Los proxies móviles y residenciales están disponibles en ambos protocolos.

Conclusión y recomendaciones

Configurar proxies en Ruby no requiere bibliotecas complejas — la estándar Net::HTTP soporta proxies HTTP "out of the box", y Faraday hace la integración aún más conveniente gracias a su arquitectura de middleware. Las conclusiones clave de esta guía son:

  • Para Net::HTTP, utiliza Net::HTTP::Proxy — es un método incorporado y confiable.
  • Para Faraday, el parámetro faraday.proxy acepta una cadena URI o un hash con credenciales.
  • Almacena los datos del proxy en variables de entorno o credenciales de Rails — no hardcodees en el código.
  • Implementa rotación de proxies y manejo de errores para un funcionamiento confiable en producción.
  • Agrega retrasos aleatorios y rotación de User-Agent para imitar el comportamiento humano.
  • Para SOCKS5, utiliza la gema socksify.

La elección del tipo de proxy depende de la tarea. Si estás escribiendo un parser para Wildberries o Ozon, los proxies residenciales ofrecerán el mejor equilibrio entre velocidad y elusión de protecciones: las IP reales de usuarios domésticos rara vez caen en listas de bloqueo. Para tareas de alta carga con un gran volumen de solicitudes a fuentes menos protegidas, los proxies de centros de datos son más rápidos y económicos por solicitud. Si tu script de Ruby trabaja con redes sociales o APIs publicitarias, considera los proxies móviles: sus direcciones IP tienen el nivel de confianza más alto en la mayoría de las plataformas.

```