15% off your first purchase with code WELCOME15, up to 25 GB.

See pricing

Integration · HTTP clients

How to use a proxy with Ruby Net::HTTP and Faraday

In Ruby, Net::HTTP takes the proxy as constructor arguments: Net::HTTP.new(host, port, proxy_host, proxy_port, proxy_user, proxy_pass). Faraday takes a proxy: hash with uri, user and password. Either way the ARC username carries the pool, country, city and an optional sticky session, so switching exits means switching usernames.

Reviewed 30 Oct 2026

your proxy, as a URL
http://USERNAME_pool-arcfast_country-de:PASSWORD@proxy.arcproxies.io:8000

The short answer

  • Net::HTTP: pass host, port, user and password as arguments 3 to 6; HTTPS targets go through a CONNECT tunnel.
  • Net::HTTP reads only http_proxy from the environment, for HTTPS targets too. Pass the proxy explicitly.
  • Faraday: proxy: { uri:, user:, password: }; for a per-request username, assign a fresh Faraday::ProxyOptions.
  • A 407 raises Net::HTTPClientException (Net::HTTP) or Faraday::ConnectionFailed (Faraday). Neither is worth retrying.

Setup

Set up Ruby with a proxy, step by step

  1. 01

    Check the exit IP with Net::HTTP

    Arguments 3 to 6 of Net::HTTP.new are the proxy host, port, user and password. Set timeouts explicitly: Net::HTTP waits 60 seconds to open a connection and 60 to read by default.

    exit_ip.rb
    require "net/http"require "json"uri = URI("https://api.ipify.org?format=json")http = Net::HTTP.new(uri.host, uri.port, "proxy.arcproxies.io", 8000, "USERNAME_pool-arcfast_country-de", "PASSWORD")http.use_ssl = truehttp.open_timeout = 10http.read_timeout = 30res = http.request(Net::HTTP::Get.new(uri))puts res.code, JSON.parse(res.body)["ip"]
  2. 02

    Keep one exit for a task

    Add _session-<handle>_sesstime-<minutes> to the username. Inside a Net::HTTP.start block every request uses one connection, so one tunnel and one exit; the session handle keeps the same exit even when a new connection is needed.

    sticky.rb
    require "net/http"require "securerandom"def with_sticky_exit(host, minutes: 10, &block)  user = "USERNAME_pool-arcfast_country-de_session-#{SecureRandom.hex(4)}_sesstime-#{minutes}"  Net::HTTP.start(host, 443, "proxy.arcproxies.io", 8000, user, "PASSWORD",                  use_ssl: true, open_timeout: 10, read_timeout: 30, &block)endwith_sticky_exit("example.com") do |http|  (1..3).each do |page|    res = http.get("/search?q=lamp&page=#{page}")    puts "#{page} #{res.code}"  endend
  3. 03

    Configure a Faraday connection

    Faraday's proxy: option takes a URL or a hash. With the default Net::HTTP adapter, each request opened its own tunnel in our test, so a rotating username can give each request a new exit. The retry middleware below retries rate limits and server errors, and by default only idempotent methods.

    faraday_client.rb
    require "faraday"require "faraday/retry"   # gem install faraday faraday-retryconn = Faraday.new(  url: "https://api.ipify.org",  proxy: { uri: "http://proxy.arcproxies.io:8000", user: "USERNAME_pool-arcfast_country-de", password: "PASSWORD" },  request: { open_timeout: 10, timeout: 30 },) do |f|  f.request :retry, max: 3, interval: 1, backoff_factor: 2,                    retry_statuses: [429, 500, 502, 503, 504]endputs conn.get("/", format: "json").body
  4. 04

    Give one Faraday request its own session

    Assign a new Faraday::ProxyOptions to req.options.proxy for the request. Do not call req.options.proxy.update(...): in our test it changed the proxy of the whole connection, so later requests kept the session username.

    faraday_sticky.rb
    require "faraday"require "securerandom"shop = Faraday.new(  url: "https://example.com",  proxy: { uri: "http://proxy.arcproxies.io:8000", user: "USERNAME_pool-arcfast_country-de", password: "PASSWORD" },  request: { open_timeout: 10, timeout: 30 },)def sticky_proxy(handle, minutes = 10)  Faraday::ProxyOptions.from(    uri: "http://proxy.arcproxies.io:8000",    user: "USERNAME_pool-arcfast_country-de_session-#{handle}_sesstime-#{minutes}",    password: "PASSWORD",  )endhandle = SecureRandom.hex(4)[1, 2, 3].each do |page|  res = shop.get("/c/lamps", page: page) { |req| req.options.proxy = sticky_proxy(handle) }  puts "#{page} #{res.status}"end
  5. 05

    Target a country or city

    Country and city are username tokens: a lowercase ISO code after _country-, and a city from the published list, lowercased without spaces, after _city-.

    geo.rb
    require "net/http"MARKETS = {  "de-munich"  => "USERNAME_pool-arcfast_country-de_city-munich",  "fr-lyon"    => "USERNAME_pool-arcfast_country-fr_city-lyon",  "ca-toronto" => "USERNAME_pool-arcfast_country-ca_city-toronto",}MARKETS.each do |market, user|  Net::HTTP.start("example.com", 443, "proxy.arcproxies.io", 8000, user, "PASSWORD",                  use_ssl: true, open_timeout: 10, read_timeout: 30) do |http|    puts "#{market} #{http.get("/price/123").code}"  endend
  6. 06

    Stop on 407, retry the rest

    For an HTTPS target, a rejected tunnel raises Net::HTTPClientException whose response.code is "407"; for a plain-HTTP target you get a 407 response. Retry timeouts, 429 and 5xx with backoff, each attempt on a new session handle.

    fetch.rb
    require "net/http"require "securerandom"class ProxyAuthError < StandardError; enddef fetch(url, attempts: 4)  uri = URI(url)  attempts.times do |i|    user = "USERNAME_pool-arcfast_country-de_session-#{SecureRandom.hex(4)}_sesstime-5"    begin      res = Net::HTTP.start(uri.host, uri.port, "proxy.arcproxies.io", 8000, user, "PASSWORD",                            use_ssl: uri.scheme == "https", open_timeout: 10, read_timeout: 30) do |http|        http.request(Net::HTTP::Get.new(uri))      end      raise ProxyAuthError, "proxy rejected the credentials" if res.code == "407"      return res unless res.code == "429" || res.code.to_i >= 500    rescue Net::HTTPClientException => e      raise ProxyAuthError, "proxy rejected the credentials" if e.response&.code == "407"      raise    rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNRESET      # fall through: a new handle on the next attempt    end    sleep(2**i)  end  raise "gave up on #{url}"endputs fetch("https://example.com/").code

Net::HTTP or Faraday: where the exit changes

PatternConnectionsExit with a rotating username
Net::HTTP.start(...) { |http| ... }One connection for the whole blockOne exit for every request in the block
A new Net::HTTP.start per requestA new tunnel each timeCan change on every request
Faraday, default Net::HTTP adapterA new tunnel per request (our test)Can change on every request
Any client + _session-<handle>AnyThe same exit for _sesstime- minutes

If a sequence of pages must come from one address, say so with a session handle instead of relying on keep-alive. Rotating vs sticky sessions covers when each fits.

Environment variables in Ruby

If you leave the proxy arguments out, Net::HTTP.new defaults to reading the proxy from the environment, and it reads http_proxy for every target, HTTPS included; https_proxy alone was ignored in our test. Uppercase HTTP_PROXY works but prints a warning. Pass nil as the proxy address to switch the environment lookup off, or the proxy explicitly as in the steps above, so a stray variable on a server cannot reroute traffic.

SOCKS5 from Ruby

Net::HTTP has no SOCKS support. Faraday's Net::HTTP adapter handles a proxy URI with the socks scheme only when the socksify gem is installed. The gateway accepts HTTP and SOCKS5 on the same port with the same credentials, so for Ruby the HTTP proxy is the simpler choice; HTTPS still runs end to end inside the CONNECT tunnel.

The problem

Common pitfalls

Setting https_proxy and expecting Net::HTTP to use it

Net::HTTP only looks at http_proxy. Pass the proxy as arguments instead.

Mutating Faraday's proxy options per request

req.options.proxy.update(user: ...) changed the connection's proxy for every later request in our test. Assign Faraday::ProxyOptions.from(...) instead.

Adding ConnectionFailed to the retry exceptions

A rejected tunnel is also Faraday::ConnectionFailed. Retrying it repeats the same wrong credentials; if you add it to exceptions:, use retry_if to skip messages containing 407.

Special characters in the password

In a proxy URL string, escape the password with URI.encode_www_form_component. The hash form (user:, password:) and the Net::HTTP arguments need no escaping.

FAQ

Questions

Something not covered? Write to contact@arcproxies.io.

How do I set a proxy with a username and password in Ruby Net::HTTP?

Pass them as arguments: Net::HTTP.new(host, port, proxy_host, proxy_port, proxy_user, proxy_pass), or the same order after host and port in Net::HTTP.start. Net::HTTP sends them as Basic proxy authentication, including on the CONNECT request for HTTPS sites.

Does Ruby Net::HTTP use the HTTPS_PROXY environment variable?

No. Net::HTTP reads http_proxy (lowercase preferred) for all targets, HTTP and HTTPS. Setting only https_proxy leaves Net::HTTP without a proxy.

How do I use a proxy with Faraday?

Pass proxy: { uri: "http://host:port", user: "name", password: "secret" } to Faraday.new, or a URL string with the credentials in it. To change it for one request, assign req.options.proxy = Faraday::ProxyOptions.from(...) in the request block.

Why does Faraday raise ConnectionFailed with 407?

The proxy refused the CONNECT tunnel because the username or password did not match. The message reads 407 "Proxy Authentication Required". Check the credentials; the faraday-retry middleware does not retry this error by default, and should not.

Sources & review

Last reviewed by the ARC Proxies team. Prices are read from the live catalog at build time.

  1. [1]Ruby: class Net::HTTP (Proxy Server, timeouts)
  2. [2]Faraday: Proxy Options
  3. [3]Faraday: Request Options
  4. [4]lostisland/faraday-retry: Faraday Retry README

Next

What people build with it

Use caseProxies for web scraping and data collectionA second provider that just works — Scrapy, requests, Crawlee or Playwright, self-serve, no sales call, bandwidth that never expires.Use caseProxies for price and competitor monitoringSee the price a shopper in Berlin, Lyon or Chicago sees. ISP bandwidth at our lowest price per GB for HTML checks, residential where a marketplace is strict.Use caseProxies for lead generation and B2B dataBuild and refresh company and contact records from public directories, listings and company sites without your one office IP becoming the bottleneck.GlossarySticky sessionA sticky session is a setting on a rotating proxy that keeps the same exit IP address for a chosen period, usually by attaching a session identifier to the connection, so a multi-step task appears to come from one address from start to finish.GlossaryHTTP CONNECTHTTP CONNECT is the HTTP request method a client sends to a proxy to ask for a tunnel to a host and port; once the proxy answers with a 2xx status it blindly forwards bytes in both directions, which is how HTTPS passes through an HTTP proxy with TLS intact end to end (RFC 9110, section 9.3.6).GuideRotating vs sticky sessionsRotating proxies change exit IP per connection; sticky sessions hold one IP for a set time. How each works, when to use which, and how to set them up.IntegrationPython requestsUse an authenticated proxy with Python requests: the proxies argument, sticky sessions per task, geo-targeting, SOCKS5 and retries that skip 407 errors.IntegrationcurlSend curl through an authenticated proxy with -x and -U, check the exit IP, test sticky sessions and countries, use socks5h, and read 407 failures.

Integrations

Other http clients

Python requestsUse an authenticated proxy with Python requests: the proxies argument, sticky sessions per task, geo-targeting, SOCKS5 and retries that skip 407 errors.HTTPXConfigure an authenticated proxy in HTTPX 0.28: the proxy= argument that replaced proxies=, mounts per domain, async clients, SOCKS5 and error handling.aiohttpUse an authenticated proxy with aiohttp: proxy= per request or per session, why proxy_auth is deprecated, SOCKS5 through aiohttp-socks, and 407 handling.AxiosUse an authenticated proxy with Axios in Node.js: the proxy option on 1.16.1+, https-proxy-agent for older versions, sticky sessions, SOCKS5 and 407s.Node.js fetchNode's built-in fetch has no proxy option. Use undici's ProxyAgent as the dispatcher, or NODE_USE_ENV_PROXY, with sticky sessions, SOCKS5 and 407 handling.curlSend curl through an authenticated proxy with -x and -U, check the exit IP, test sticky sessions and countries, use socks5h, and read 407 failures.GoRoute Go's net/http through an authenticated proxy: Transport.Proxy with url.UserPassword, a sticky session per request context, SOCKS5 and 407 checks.PHP GuzzleRoute PHP Guzzle and plain cURL through an authenticated proxy: the proxy option, sticky sessions, geo-targeting, retries that stop on a 407.Java HttpClientUse an authenticated proxy with java.net.http.HttpClient: ProxySelector, Authenticator, the Basic-over-CONNECT setting, sticky sessions and 407s.C# HttpClientUse an authenticated proxy in .NET: WebProxy and NetworkCredential on SocketsHttpHandler, a client per sticky session, SOCKS5 and 407 handling.Rust reqwestRoute Rust reqwest through an authenticated proxy: Proxy::all with basic_auth, a Client per sticky session, socks5h, timeouts and 407 errors.wgetRun GNU Wget through an authenticated proxy: https_proxy, --proxy-user, a .wgetrc for credentials, sticky sessions for mirrors and exit codes.PostmanSend Postman requests through an authenticated proxy: the custom proxy settings, Proxy auth, checking the exit IP, sessions, countries and Newman.DockerGive Docker containers and builds an authenticated proxy: HTTP_PROXY, HTTPS_PROXY and NO_PROXY, build args, Compose, and why the daemon proxy differs.

Get started

Get credentials and paste them in.

Sign up, buy a gigabyte, generate a username in the dashboard. Code WELCOME15 takes 15% off your first purchase; bandwidth never expires.