Back to Blog

Setting Up Proxies in Ruby with Net::HTTP and Faraday: Complete Guide for Rails and Standalone Scripts

How to correctly configure a proxy in Ruby projects using Net::HTTP and Faraday — with examples for Rails and standalone scripts, IP rotation, and bypassing blocks.

📅August 13, 2026

If you are writing a parser, integrating an external API, or automating data collection in Ruby — sooner or later you will encounter IP blocks. Wildberries, Ozon, Instagram, and dozens of other services limit requests from a single address after just a few hundred calls. The solution is simple — proxies. In this article, we will explore how to connect proxies to a Ruby project using the standard Net::HTTP and the popular HTTP client Faraday, set up IP rotation, and properly handle errors — both in a Rails application and in simple scripts.

Why Ruby Developers Need Proxies: Main Scenarios

Ruby is actively used for tasks where proxies are simply indispensable. Before diving into the code, it's worth understanding in which situations they are needed — this will help you choose the right type and architecture of the solution.

Parsing and Data Collection. This is the most common scenario. You are writing a Ruby script that scrapes pages from Wildberries, Ozon, or Avito, collecting prices and product characteristics. Without proxies, such a script will be blocked by IP in just a few minutes. Websites see hundreds of requests from a single address and automatically enable protection — CAPTCHA, temporary bans, or complete denial of service.

Integration with Geo-dependent APIs. Some external services return different content depending on the request's country. If your Rails application accesses such an API, proxies allow you to simulate requests from the desired region. For example, checking search engine results for different countries or obtaining prices relevant to a specific market.

Testing and QA. Developers use proxies to test application behavior when requests come from different IP addresses and countries. This is especially relevant for services with geo-blocking or rate-limiting by IP.

Marketing Automation. Ruby scripts are often used to monitor advertising campaigns, check rankings in search results, or track competitor activity. Here, proxies are needed to distribute the load and bypass restrictions.

Working with Multiple Accounts. If your script manages multiple accounts on one platform, each account must operate through a separate IP — otherwise, the platform can easily detect the connection and block everything at once.

Which Type of Proxy to Choose for Ruby Tasks

Not all proxies are equally suitable for different tasks. The choice of type directly affects the success of your script's operation. Let's discuss the main options relevant to Ruby development.

Proxy Type Best Tasks in Ruby Speed Bypassing Protections
Datacenter Proxies Parsing without strict protection, API requests, testing High Medium
Residential Proxies Parsing protected sites, working with social media, marketplaces Medium High
Mobile Proxies Facebook, Instagram, TikTok API, account management Medium Maximum

Datacenter Proxies are the fastest and cheapest option. They are suitable for scraping sites without aggressive protection, mass API requests, and load testing. However, large platforms (Facebook, Google, Cloudflare) can easily recognize them by the ASN of the datacenter.

Residential Proxies use IPs of real home users. For Ruby scripts that scrape Wildberries, Ozon, or work with protected sites — this is the optimal choice. Websites see a regular user, not a server.

Mobile Proxies operate through IPs of mobile operators (4G/5G). These are the most trusted addresses from the platform's perspective — a single mobile IP can be used by hundreds of real users simultaneously, so blocks on them are extremely rare. If your Ruby script works with social networks or advertising platforms — mobile proxies will minimize the risk of blocks.

Proxies in Net::HTTP: Basic Setup

Net::HTTP is the standard Ruby library for HTTP requests, included in the standard distribution of the language. It supports proxies "out of the box" through a special method Net::HTTP::Proxy.

The simplest way is to create a proxy class and use it instead of the regular Net::HTTP:

require 'net/http'
require 'uri'

# Proxy data
proxy_host = 'proxy.example.com'
proxy_port = 8080

# Create a proxy class
proxy_class = Net::HTTP::Proxy(proxy_host, proxy_port)

# Execute a request through the 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

The Net::HTTP::Proxy method takes four parameters: proxy host, port, username, and password. The first two are mandatory, the last two are optional for proxies without authentication.

An alternative way is to pass proxy parameters directly to the Net::HTTP.new method:

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 if no authentication)
  nil                    # proxy_pass
)

http.use_ssl = true

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

This approach is convenient when you need to reuse the same HTTP object for multiple requests to the same host. Note that proxy parameters are passed as the third and fourth arguments, not through a separate method.

Authentication and HTTPS via Net::HTTP

Most commercial proxies require authentication — a username and password. Additionally, when working with HTTPS sites, extra SSL configuration is needed. Let's discuss both cases.

Proxies with Username and Password:

require 'net/http'
require 'uri'

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

# Create a proxy class with authentication
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|
  # Disable SSL verification (for testing only!)
  # 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

An important point: when using HTTPS proxies (tunneling via CONNECT), Net::HTTP automatically establishes an SSL connection with the target server through the proxy tunnel. This works correctly for most proxy servers.

Configuration via Environment Variables. A good practice is to avoid hardcoding proxy data in the code and instead read them from environment variables:

require 'net/http'
require 'uri'

# Read proxy settings from 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

The environment variable can be set in the format HTTP_PROXY=http://user:[email protected]:8080. This approach allows you to change the proxy without modifying the code — simply update the variable in the .env file or in the server settings.

Connecting Proxies via Faraday

Faraday is one of the most popular HTTP clients in the Ruby ecosystem. It is used in many Rails applications due to its convenient middleware approach and support for various adapters (Net::HTTP, HTTParty, Typhoeus, and others). Setting up proxies in Faraday differs slightly depending on the adapter used.

Basic Proxy Setup in Faraday:

require 'faraday'

# Create a Faraday connection with a 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}"

The faraday.proxy parameter accepts a hash with keys uri, user, and password. You can also pass a URI string directly if the proxy does not require authentication.

Faraday with Net::HTTP Adapter and Advanced Settings:

require 'faraday'

conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
  faraday.proxy = 'http://user:[email protected]:8080'
  
  # Timeout settings
  faraday.options.timeout = 30
  faraday.options.open_timeout = 10
  
  # Middleware for logging (useful for debugging)
  faraday.response :logger
  
  # Retry on connection errors
  faraday.request :retry, max: 3, interval: 1
  
  # Adapter
  faraday.adapter :net_http
end

begin
  response = conn.get('/ip')
  puts response.body
rescue Faraday::ConnectionFailed => e
  puts "Connection error: #{e.message}"
rescue Faraday::TimeoutError => e
  puts "Timeout: #{e.message}"
end

The :retry middleware is particularly useful when working with proxies — if one address is temporarily unavailable, Faraday will automatically retry the request. This is critical for reliable script operation in production.

Faraday with Typhoeus Adapter (for parallel requests):

# 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

# Parallel requests through the 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 }

Proxy Rotation: Automatic IP Change

One proxy address, even a residential one, will eventually fall under restrictions during intensive scraping. The solution is rotation: automatic IP change after each request or at certain intervals. Let's implement a simple rotator for Ruby scripts.

Simple Rotator Based on an Array of 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

# List of proxies in the format [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)

# Scraping a list of URLs through different 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 with proxy #{proxy[0]}: #{e.message}"
    # Automatically move to the next proxy
  end
  
  sleep(rand(0.5..2.0)) # Random delay between requests
end

A random delay between requests (sleep(rand(0.5..2.0))) is an important element. The pattern of requests with fixed intervals is easily detected by anti-bot systems. Random delays simulate real user behavior.

Rotation via Faraday with 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)
    # Choose a random proxy for each request
    proxy_uri = URI(PROXIES.sample)
    
    env[:request][:proxy] = {
      uri: proxy_uri,
      user: proxy_uri.user,
      password: proxy_uri.password
    }
    
    @app.call(env)
  end
end

# Register 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

Integrating Proxies into a Rails Application

In a Rails application, proxies are most often needed for external HTTP requests: API integrations, background parsing tasks, or webhooks. Let's consider several practical patterns.

Pattern 1: Service Object with Faraday. Create a basic service class that makes all external HTTP requests through a proxy:

# app/services/http_client.rb
class HttpClient
  def self.connection(base_url)
    Faraday.new(url: base_url) do |faraday|
      # Proxy from Rails environment variables
      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

# Usage in a controller or service:
# client = HttpClient.connection('https://api.example.com')
# response = client.get('/products', { category: 'electronics' })

Pattern 2: Configuration via Rails Credentials. For storing proxy data, use encrypted Rails credentials:

# config/credentials.yml.enc (edited via 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

Pattern 3: Background Tasks with Sidekiq. If parsing or API requests are performed in background workers, the proxy setup remains the same, but it is important to consider concurrency:

# app/workers/price_parser_worker.rb
class PriceParserWorker
  include Sidekiq::Worker
  
  sidekiq_options retry: 3, queue: :parsers
  
  def perform(product_url)
    # Create a new connection for each worker
    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 will retry the task
  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)
    # Parsing logic...
  end
end

Error Handling and Timeouts

Working through proxies adds an additional layer that can generate specific errors. Proper exception handling is the difference between a script that crashes after 5 minutes and one that runs for hours without intervention.

Main Types of Errors When Working with Proxies in Ruby:

Exception Cause What to Do
Net::OpenTimeout Proxy is not responding Change proxy, retry
Net::ReadTimeout Proxy is slow or the site is not responding Increase timeout or change proxy
Errno::ECONNREFUSED Proxy server refused the connection Check proxy data
OpenSSL::SSL::SSLError SSL issue through the proxy Check SSL settings
HTTP 407 Invalid proxy username/password Check credentials
HTTP 403/429 IP blocked by the site Change proxy, add a pause

Comprehensive Error Handling with Automatic Proxy Change:

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 Proxies in Ruby: Setup via Socksify

The standard Net::HTTP does not natively support SOCKS5 — only HTTP/HTTPS proxies. To work with SOCKS5, an additional library is needed. The most popular option is the socksify gem.

Why might SOCKS5 be preferable to HTTP proxies? The SOCKS5 protocol operates at a lower level — it proxies any TCP traffic, not just HTTP. Additionally, SOCKS5 supports authentication and transmits less metadata about the request, making it less detectable by bot detection systems.

# Gemfile
# gem 'socksify'

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

# Global SOCKS5 proxy settings
TCPSocket::socks_server = 'proxy.example.com'
TCPSocket::socks_port = 1080

# Optionally: authentication (SOCKS5 username/password)
# TCPSocket::socks_username = 'your_user'
# TCPSocket::socks_password = 'your_pass'

# Now all Net::HTTP requests go through SOCKS5
uri = URI('https://httpbin.org/ip')
response = Net::HTTP.get_response(uri)
puts response.body

Note: when using socksify, the configuration applies globally to all TCP connections in the process. If you need more flexible settings — use Net::HTTP.SOCKSProxy from the same gem:

require 'socksify/http'

# Create a SOCKS5 proxy class (similar to Net::HTTP::Proxy for 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

For Faraday with SOCKS5, you will need an adapter that supports this protocol. A good option is faraday-net_http in conjunction with socksify, or using the curl-based adapter faraday-typhoeus, which natively supports SOCKS5 through libcurl.

💡 Protocol Selection Tip

For most scraping and API tasks, HTTP/HTTPS proxies are sufficient — they are easier to set up and supported natively. SOCKS5 should be used when you need to proxy not just HTTP traffic, or when HTTP proxies are easily detected by the target service. Mobile and residential proxies are available in both protocols.

Conclusion and Recommendations

Setting up proxies in Ruby does not require complex libraries — the standard Net::HTTP supports HTTP proxies "out of the box," and Faraday makes integration even more convenient thanks to its middleware architecture. Key takeaways from this guide:

  • For Net::HTTP, use Net::HTTP::Proxy — this is a built-in and reliable method.
  • For Faraday, the faraday.proxy parameter accepts a URI string or a hash with credentials.
  • Store proxy data in environment variables or Rails credentials — do not hardcode them in the code.
  • Implement proxy rotation and error handling for reliable operation in production.
  • Add random delays and rotate User-Agent to simulate human behavior.
  • For SOCKS5, use the socksify gem.

The choice of proxy type depends on the task. If you are writing a parser for Wildberries or Ozon — residential proxies will provide the best balance between speed and bypassing protections: real home user IPs rarely end up in blocklists. For high-load tasks with a large number of requests to less protected sources, datacenter proxies will be faster and cheaper per request. If your Ruby script works with social networks or advertising APIs — consider mobile proxies: their IP addresses have the highest level of trust on most platforms.