Pass the proxy to Net::HTTP.new after the host and port: Net::HTTP.new(host, port, p_addr, p_port, p_user, p_pass). Every request made through that object then goes via the proxy. Leave the proxy out and p_addr falls back to :ENV, so Ruby reads the http_proxy variable; pass nil to stop that. Faraday takes a proxy: option, HTTParty takes http_proxyaddr and three sibling options, and Mechanize has set_proxy. For SOCKS5 you add the socksify gem.
The arguments have grown since Ruby 2.3, and the way Ruby treats a login inside http_proxy has changed twice. The table below is built from Ruby's documentation and source at each release. Every code block comes from the library's own documentation, read on October 13, 2026. We ran none of it for this page. Results from the tests in our earlier Ruby web scraping guide appear below, marked as such.
What do p_addr, p_port, p_user and p_pass do?
Ruby 4.0 documents the full signature as new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil, p_use_ssl = nil). The first two name the site you want. The rest describe the proxy.
The arguments of Net::HTTP.new in Ruby 4.0
| Argument | Default | What it is |
|---|---|---|
| address | required | The site you call, never the proxy |
| port | nil | The site's port; nil means 80 |
| p_addr | :ENV | Proxy host; :ENV reads http_proxy, nil means no proxy |
| p_port | nil | Proxy port |
| p_user | nil | Proxy user name |
| p_pass | nil | Proxy password |
| p_no_proxy | nil | Hosts that skip the proxy (Ruby 2.5 and later) |
| p_use_ssl | nil | TLS to the proxy itself (Ruby 3.4 and later) |
Ruby 4.0 documentation, class Net::HTTP, read October 13, 2026
Ruby 2.3's documentation defines the four in one sentence: "If you are connecting to a custom proxy, p_addr the DNS name or IP address of the proxy host, p_port the port to use to access the proxy, and p_user and p_pass the username and password if authorization is required to use the proxy". Its example passes a host and a port only.
proxy_addr = 'your.proxy.host'
proxy_port = 8080
Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http|
# always proxy via your.proxy.addr:8080
}
Ruby 4.0's documentation adds a login and reads it all back. This is its example, with its placeholder names.
http = Net::HTTP.new(hostname, nil, 'proxy.example', 8000, 'pname', 'ppass')
# => #<Net::HTTP jsonplaceholder.typicode.com:80 open=false>
http.proxy? # => true
http.proxy_from_env? # => false
http.proxy_address # => "proxy.example"
http.proxy_port # => 8000
http.proxy_user # => "pname"
http.proxy_pass # => "ppass"
Those accessors are the cheapest check you have. Before a single request leaves your machine, proxy? and proxy_address tell you which proxy the object will use, and proxy_from_env? tells you whether it came from the environment.
There is an older spelling, Net::HTTP::Proxy(p_addr, p_port, p_user, p_pass), which returns a subclass bound to one proxy. Ruby 2.3's source already calls it obsolete: "You may pass these same parameters directly to Net::HTTP.new". It still ships in net-http 0.10.0, with the same comment, and the Ruby 4.0 documentation gives it no entry of its own.
What changed between Ruby 2.3 and 4.0
The arguments and the handling of a login inside http_proxy both moved over the years. We read the documentation of each release on docs.ruby-lang.org and the file lib/net/http.rb at each release tag.
Net::HTTP proxy support by Ruby release
| Ruby | Proxy arguments after host and port | A login inside http_proxy |
|---|---|---|
| 2.3, 2.4 | p_addr, p_port, p_user, p_pass | Ignored: pass p_user and p_pass |
| 2.5 | adds p_no_proxy | Ignored |
| 2.6 to 3.1 | same as 2.5 | Used on Linux, FreeBSD and macOS only |
| 3.2, 3.3 | same as 2.5 | Used everywhere, percent-decoded |
| 3.4, 4.0 | adds p_use_ssl | Used everywhere, percent-decoded |
docs.ruby-lang.org and lib/net/http.rb at each release tag, read October 13, 2026
Ruby 2.3's documentation says it plainly: "If the proxy requires authentication you must supply it by hand". From Ruby 2.6, a user and password inside http_proxy were read, but only on Linux, FreeBSD and macOS. The source ties that limit to Ruby bug 12921. Ruby 3.2 dropped the platform check. Ruby 3.4 added p_use_ssl, which the documentation describes as "Use SSL when talking to the proxy".
Why is Net::HTTP using a proxy I did not set?
The default value of p_addr is the symbol :ENV. Ruby 2.3's documentation states the result: "Net::HTTP will automatically create a proxy from the http_proxy environment variable if it is present". Ruby 4.0's documentation adds a condition: "note that the URI string must have a protocol such as 'http' or 'https'". A request you thought was direct can therefore leave through a proxy set in your shell or your CI. On Stack Overflow, the highest-scored answer to the question "How to set a proxy in rubys net/http?", viewed 39,620 times, gives the same warning and the same way out.
Four details of that lookup surprise people.
- One variable for every target. Net::HTTP builds an
httpURI for the lookup, so it readshttp_proxyeven when the site is https.https_proxyplays no part. - The uppercase name. Ruby's
find_proxyreadsHTTP_PROXYtoo, and Ruby 4.0 warns "The environment variable HTTP_PROXY is discouraged. Please use http_proxy instead". Under CGI it skipsHTTP_PROXY, "because HTTP_PROXY may be set by Proxy: header". - Exceptions.
no_proxylists hosts that go direct, and a site whose address starts with 127. or is ::1 never goes through the proxy. In the tests of our Ruby web scraping guide, Net::HTTP, HTTParty and Faraday went direct for that reason, even to shop.localtest.me, a public name for 127.0.0.1. - The one-line helpers.
Net::HTTP.getandNet::HTTP.postcallstartwith only the URL's host and port, so they can only use a proxy from the environment.
To make sure a connection goes direct, pass nil as the proxy address: Net::HTTP.new(host, port, nil). The 2.3 entry for new gives the rule in one line: "To disable proxy detection set p_addr to nil".
HTTPS through a proxy: use_ssl and certificates
Net::HTTP.new takes a host name, so it never sees whether you meant https. You set use_ssl = true yourself, or pass use_ssl: true to Net::HTTP.start, which also turns on certificate checks (verify_mode defaults to VERIFY_PEER there). With a proxy set, Net::HTTP then sends CONNECT host:port to the proxy and runs TLS with the site through that tunnel. When a proxy user is set, the CONNECT carries a Proxy-Authorization: Basic header. A plain HTTP proxy therefore carries https traffic. Unless you set p_use_ssl, that CONNECT line, with the site's host and port, and the proxy login reach the proxy unencrypted. Your request to the site travels inside the TLS session.
Net::HTTP object
the proxy is set on it
CONNECT to the proxy
Proxy-Authorization: Basic if a user is set
TLS with the site
inside the tunnel
The site
sees the proxy's address
When the proxy answers 407
If the login is wrong, the proxy answers 407. RFC 9110 defines it as a status that "indicates that the client needs to authenticate itself in order to use a proxy for this request". Our 407 guide walks through the causes.
Certificate errors only behind the proxy
One known cause of a certificate error that appears only behind the proxy is a proxy that reads your TLS traffic. Debugging proxies work this way. The accepted answer to the Stack Overflow question "certificate verify failed ONLY WHEN PROXYING", viewed 11,022 times, traces such an error to Charles. Charles describes its method: it "dynamically generates a certificate for the server and signs it with its own root certificate". Give Net::HTTP that root certificate through ca_file, which takes "the path to a CA certification file in PEM format". Turning verification off fixes the error by removing the check, so keep it on.
How do I set a proxy in Faraday?
Faraday 2.14.4, released on September 15, 2026, takes proxy: as "Proxy options, either as a URL or as a Hash of" proxy options. The hash has three keys, :uri, :user and :password. This is the example from Faraday's proxy documentation.
# Proxy options can be passed to the connection constructor and will be applied to all requests.
proxy_options = {
uri: 'http://proxy.example.com:8080',
user: 'username',
password: 'password'
}
conn = Faraday.new(proxy: proxy_options) do |faraday|
# ...
end
# You can then override them on a per-request basis.
conn.get('/foo') do |req|
req.options.proxy.update(uri: 'http://proxy2.example.com:8080')
end
The last block matters for rotation: one connection can send each request through a different proxy. When you pass no proxy, Faraday asks Ruby's find_proxy for the request's own URL. An https URL therefore picks up https_proxy, and no_proxy applies. Faraday.ignore_env_proxy = true switches that lookup off. Its source comment reads "Tells Faraday to ignore the environment proxy (http_proxy)", and it starts as false.
HTTParty, Mechanize and the other clients
HTTParty
HTTParty 0.24.3 lists four proxy settings among its common request options: http_proxyaddr, http_proxyport, http_proxyuser and http_proxypass. As request options, they can travel with a single call. Its class method http_proxy sets all four for every request of a class. Its README and docs page show no proxy example, so this one comes from the comments in its source.
class Foo
include HTTParty
http_proxy 'http://foo.com', 80, 'user', 'pass'
end
HTTParty hands these values to Net::HTTP.new only when the http_proxyaddr key is present. Without it, it calls Net::HTTP.new(host, port), and the http_proxy lookup above applies. Note that the example passes a full URL as the address, where Ruby documents p_addr as a host name.
Mechanize, Excon, http.rb, Typhoeus and rest-client
Mechanize 2.14.1 documents set_proxy address, port, user = nil, password = nil, which sets the proxy's address and port with an optional user and password. The other clients name the proxy in their own way. Each pair of lines below comes from the client's README or wiki.
# Excon (README)
connection = Excon.new('http://geemus.com', :proxy => 'http://my.proxy:3128')
connection.request(:method => 'GET')
# http.rb (wiki, Proxy Support)
HTTP.via("proxy-hostname.local", 8080, "username", "password")
.get("http://example.com/resource")
# Typhoeus (README)
options = {proxy: 'http://proxyurl.com', proxyuserpwd: 'user:password'}
req = Typhoeus::Request.new(url, options)
# rest-client (README)
RestClient.proxy = "http://proxy.example.com/"
RestClient.get "http://some/resource"
Excon carries two warnings worth knowing. It "will use HTTP_PROXY and HTTPS_PROXY environment variables. If set they will take precedence over any :proxy option specified in code". Its proxy also "must be set when establishing a connection object and cannot be overridden in individual requests". Typhoeus hands its requests to libcurl. In the tests of our scraping guide, libcurl sent requests through the proxy in http_proxy even to a local target, where Net::HTTP, HTTParty and Faraday went direct.
rest-client 2.1.0 dates from August 2019, the newest of its releases on RubyGems. Its README says a per-request proxy: overrides "any proxies set by environment variable". OpenURI, part of Ruby, takes :proxy and :proxy_http_basic_authentication, and raises ArgumentError when both are given.
All nine clients side by side
How each Ruby client takes a proxy
| Client | Proxy setting | Without it |
|---|---|---|
| Net::HTTP | Arguments after the port | Reads http_proxy |
| OpenURI | :proxy option | Reads http_proxy or https_proxy |
| Faraday 2.14.4 | proxy: URL or hash | Asks find_proxy |
| HTTParty 0.24.3 | Four http_proxy options | Net::HTTP reads http_proxy |
| Mechanize 2.14.1 | set_proxy | Not stated |
| Excon 1.7.2 | proxy: URL | HTTP_PROXY wins over code |
| http 6.0.4 | HTTP.via | Not stated |
| Typhoeus 1.6.0 | proxy: plus a login option | libcurl used http_proxy in our test |
| rest-client 2.1.0 | A global setting or proxy: | Environment proxies apply |
Each client's documentation and source, read October 13, 2026; versions from RubyGems; Typhoeus from our Ruby web scraping guide
Download counts include CI machines and mirrors, and net-http ships inside Ruby, so its gem count understates its use. They still put Faraday far ahead of the others.
How do I use a SOCKS5 proxy in Ruby?
Net::HTTP talks only to HTTP proxies. The socksify gem fills the gap. Its README says it works "by patching a private method in Net::HTTP, as sadly Ruby no longer has native socks proxy support out of the box". The gem "redirects any TCP connection initiated by a Ruby script through a SOCKS5 proxy". These two blocks come from the README.
require 'socksify/http'
uri = URI.parse('http://ipecho.net/plain')
Net::HTTP.socks_proxy('127.0.0.1', 9050).start(uri.host, uri.port) do |http|
req = Net::HTTP::Get.new uri
resp = http.request(req)
puts resp.inspect
puts resp.body
end
require 'socksify/http'
uri = URI.parse('http://ipecho.net/plain')
Net::HTTP.socks_proxy('127.0.0.1', 1080, username: 'my_username', password: 'my_pwd').start(uri.host, uri.port) do |http|
req = Net::HTTP::Get.new uri
resp = http.request(req)
puts resp.inspect
puts resp.body
end
The README marks the older way, setting TCPSocket.socks_server and TCPSocket.socks_port, as "Deprecated in Ruby 3.1 onwards". It adds that Net::HTTP.socks_proxy "never relies on" those two settings. The current release is socksify 1.8.1, from July 2025.
What socksify's source shows
In the gem's source, a host name goes to the proxy as a SOCKS5 domain name, so the proxy does the DNS lookup. That matches the README's line that the gem "doesn't leak DNS queries". An IPv6 address stops with the error "TCP/IPv6 over SOCKS is not yet supported". For an https site, pass use_ssl: true to start as usual, and TLS runs inside the SOCKS connection. Our page on what a SOCKS5 proxy is explains remote DNS.
How do I rotate proxies in Ruby?
Net::HTTP holds the proxy on the object. Its documentation lists writers such as proxy_address= and proxy_port= to change it, or you keep one object per proxy. Faraday is easier: one connection, and req.options.proxy.update per request, as in its documentation above. HTTParty takes the four proxy options on each call. Excon needs one connection per proxy, since its proxy cannot change per request.
Connections matter here too. With the proxy named in the code, our scraping guide's test saw HTTParty and Faraday open a new connection for every page. A Net::HTTP.start block and Typhoeus kept one connection for all 20 pages.
For a pool to try, our free proxy API returns "every working proxy we have" in one GET to https://hproxy.com/api/proxy-list, with no key. Its documented parameters include protocol, limit and format, which defaults to txt with one ip:port per line. The free proxy list shows the same proxies in a browser. The API's documentation says the pool "moves every few minutes as proxies die and new ones are found", so give every request a timeout and expect failures.
How do I check that Ruby really uses the proxy?
Check the object before you send anything. proxy?, proxy_address and proxy_from_env? show what Net::HTTP will do, and catch the case where http_proxy decided for you. Then ask an IP echo service for your address through the object, and compare it with the answer of a direct request.
Our Ruby web scraping guide asked for 20 pages with Net::HTTP, HTTParty, Faraday and Typhoeus. A counting proxy of our own sat first in http_proxy, then in the code. All of it ran on our own server. The proxy checker page offers our free app, which tests proxy lists from your own connection and shows the fraud score of your exit.
The common failures
- A proxy you never set.
http_proxywas set in the shell or CI. Passnilasp_addr, or unset the variable. - A proxy that is never used. The value in
http_proxyhas no scheme, or the site is onno_proxyor on 127.x. - A 407 on every request. The login is wrong, or Ruby ignored the one in
http_proxy(Ruby 2.5 and older, or Windows before 3.2). Passp_userandp_passdirectly. - A SOCKS5 address handed to Net::HTTP. Ruby has no SOCKS support of its own, in socksify's words. Use socksify.
- Certificate errors only behind the proxy. The proxy reads TLS. Trust its root certificate with
ca_file. - Excon ignores your proxy option.
HTTP_PROXYorHTTPS_PROXYis set, and Excon lets them win.
The cURL guide tests a proxy from the shell before you wire it into Ruby. The Python requests guide covers the same ground in Python, and proxies for web scraping covers which kind of proxy suits which job.
Where HProxy fits
Every client on this page takes an HTTP proxy as a host, a port, a user and a password. Our residential proxies start at $0.44 per GB on Lite, with rotating addresses, and Premium adds sticky sessions of up to 24 hours from $1.00 per GB. Datacenter proxies with HTTP, HTTPS and SOCKS5 start at $1.58 per IP. The pricing page has every plan, and the API reference documents the endpoints.
What we could not find out
- Behavior we did not run. Under our rules for this page we executed no code and used no proxy. Every statement about behavior comes from documentation, source code or the earlier tests of our scraping guide.
- HTTParty and a full URL. HTTParty's example passes
'http://foo.com'as the address. The code hands that value to Net::HTTP unchanged, and Ruby documentsp_addras a host name. We did not test whether a URL works there, so a bare host is the safe choice. - socksify and http_proxy.
Net::HTTP.starthands:ENVto the class socksify builds, and that class does not mark itself as a proxy class. The code we read suggests that a sethttp_proxywould then take over from the SOCKS proxy. We did not run it, and the README does not mention it, so unsethttp_proxyin a process that uses socksify. - Silent clients. The documentation of Mechanize and http.rb that we read says nothing about
http_proxy. - The end of file reached error. Two Stack Overflow threads from 2022 report it through a proxy. One has no accepted answer, and the accepted answer of the other swaps Faraday's adapter for Typhoeus without naming a cause.
- Moving versions. net-http, Faraday and HTTParty all released in the four weeks before October 13, 2026, the day we checked every version here.
Sources
All read on October 13, 2026.
- Ruby documentation, class Net::HTTP, for Ruby 4.0 and Ruby 2.3, and the same page for Ruby 3.4, 3.3, 2.5 and 2.4.
- Ruby 4.0 documentation, class URI::Generic and module OpenURI.
- Ruby source, lib/net/http.rb at the release tags v2_3_0, v2_6_0, v2_7_0, v3_0_0, v3_1_0, v3_2_0, v3_3_0 and v3_4_0, and the ruby/net-http repository at version 0.10.0.
- Faraday documentation, Proxy Options and Connection Options, and its lib/faraday.rb and lib/faraday/connection.rb (version 2.14.4).
- HTTParty: README, docs page, lib/httparty.rb and lib/httparty/connection_adapter.rb (version 0.24.3).
- Mechanize, lib/mechanize.rb (version 2.14.1).
- socksify: README and the source files lib/socksify/http.rb, tcpsocket.rb, socksproxyable.rb and ruby3net_http_connectable.rb (version 1.8.1).
- Excon README (1.7.2); http.rb wiki, Proxy Support (6.0.4); Typhoeus README (1.6.0); rest-client README (2.1.0).
- RubyGems API, the records of the nine gems above.
- RFC 9110, HTTP Semantics, section 15.5.8, 407 Proxy Authentication Required (IETF, June 2022).
- Charles Web Debugging Proxy documentation, SSL Proxying.
- Stack Overflow questions 15792999, 19463827, 70727302 and 72511061 with their answers, read through the Stack Exchange API.
- Our own pages: the free proxy list API documentation, and the proxy section of our Ruby web scraping guide.



