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
http://USERNAME _pool-arcfast _country-de:PASSWORD@ proxy.arcproxies.io:8000The 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_proxyfrom the environment, for HTTPS targets too. Pass the proxy explicitly. - Faraday:
proxy: { uri:, user:, password: }; for a per-request username, assign a freshFaraday::ProxyOptions. - A 407 raises
Net::HTTPClientException(Net::HTTP) orFaraday::ConnectionFailed(Faraday). Neither is worth retrying.
Setup
Set up Ruby with a proxy, step by step
- 01
Check the exit IP with Net::HTTP
Arguments 3 to 6 of
Net::HTTP.neware 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.rb11 linesrequire "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"] - 02
Keep one exit for a task
Add
_session-<handle>_sesstime-<minutes>to the username. Inside aNet::HTTP.startblock 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.rb15 linesrequire "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 - 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.rb13 linesrequire "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 - 04
Give one Faraday request its own session
Assign a new
Faraday::ProxyOptionstoreq.options.proxyfor the request. Do not callreq.options.proxy.update(...): in our test it changed the proxy of the whole connection, so later requests kept the session username.faraday_sticky.rb22 linesrequire "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 - 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.rb14 linesrequire "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 - 06
Stop on 407, retry the rest
For an HTTPS target, a rejected tunnel raises
Net::HTTPClientExceptionwhoseresponse.codeis"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.rb28 linesrequire "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
| Pattern | Connections | Exit with a rotating username |
|---|---|---|
Net::HTTP.start(...) { |http| ... } | One connection for the whole block | One exit for every request in the block |
A new Net::HTTP.start per request | A new tunnel each time | Can change on every request |
| Faraday, default Net::HTTP adapter | A new tunnel per request (our test) | Can change on every request |
Any client + _session-<handle> | Any | The 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]Ruby: class Net::HTTP (Proxy Server, timeouts)
- [2]Faraday: Proxy Options
- [3]Faraday: Request Options
- [4]lostisland/faraday-retry: Faraday Retry README
Next
What people build with it
Integrations
Other http clients
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.