HTTP/3 & UDP over SOCKS5
Updated:
Residential proxies carry UDP traffic over SOCKS5. Use it to open websites over HTTP/3 (QUIC) from a residential IP, and to route any other UDP protocol: DNS, WebRTC, VoIP, online games.
Works on every list you already have β same host, same ports, same login and password. There is nothing to enable and nothing to order.
How it works
HTTP/3 runs over QUIC, and QUIC runs over UDP. The HTTP proxy protocol carries TCP only, so UDP traffic goes through SOCKS5.
Traffic | HTTP proxy | SOCKS5 |
|---|---|---|
TCP: HTTP/1.1, HTTP/2, any other TCP protocol | β | β |
UDP: HTTP/3 (QUIC), DNS, WebRTC, VoIP, games | β | β |
Three exchanges happen before the first datagram leaves your machine:
Connect and authenticate
Your app opens a SOCKS5 connection to the gateway and logs in with the login and password of your list.
Request a UDP relay
It sends a
UDP ASSOCIATErequest (RFC 1928). The gateway answers with a relay address: the gateway IP and a port in the 30000β39999 range.Send datagrams
The app sends UDP datagrams to that relay. Each datagram starts with a short SOCKS5 header naming the destination. The traffic leaves from the residential exit IP, and the answers come back the same way.
The relay lives as long as the SOCKS5 TCP connection stays open. Close the control connection and the relay closes with it.
Connection settings
Setting | Value |
|---|---|
Protocol | SOCKS5 with username/password authentication |
Host |
|
Port | any port from your list (10000β10999) |
Login and password | the login and password of your list |
UDP relay | the gateway IP, ports 30000β39999 |
HTTP and SOCKS5 share the same host, ports, login and password β the gateway detects the protocol by itself. To move a connection string to SOCKS5, change the scheme and nothing else:
http://LOGIN:[email protected]:10000
socks5://LOGIN:[email protected]:10000The gateway is only the entry point. The exit IP and its location come from the list settings, whichever gateway you use β so pick the gateway with the lowest latency from your server.
If your network restricts outgoing traffic, allow UDP to the gateway on ports 30000β39999. A blocked range is the usual reason the UDP check below times out.
Check it yourself
Check the connection
Any SOCKS5 client will do β
curlis enough:curl -x socks5h://LOGIN:[email protected]:10000 https://api.ipify.orgThe response is the proxy's exit IP. With
socks5h://, domain names are resolved by the proxy, not by your machine.Check UDP
This script uses only the Python standard library. It logs in over SOCKS5, opens a UDP relay and sends one DNS query through it. The OpenDNS resolver answers
myip.opendns.comwith the address the query came from, so the script prints the exit IP of your UDP traffic.Save it as
socks5_udp.py, set your login and password, and runpython socks5_udp.py."""UDP through a residential proxy: SOCKS5 login, UDP ASSOCIATE and one DNS query. The OpenDNS resolver answers "myip.opendns.com" with the IP the query came from, so the script prints the exit IP your UDP traffic leaves from. """ import os import socket import struct PROXY_HOST = "res.proxy-seller.com" PROXY_PORT = 10000 LOGIN = "LOGIN" PASSWORD = "PASSWORD" def recv_exact(sock, n): data = b"" while len(data) < n: chunk = sock.recv(n - len(data)) if not chunk: raise ConnectionError("proxy closed the connection") data += chunk return data def udp_associate(host, port, login, password): """SOCKS5 login (RFC 1929) and UDP ASSOCIATE (RFC 1928). Returns the TCP control connection and the UDP relay address. The relay works while the TCP connection stays open. """ tcp = socket.create_connection((host, port), timeout=15) tcp.sendall(b"\x05\x01\x02") # SOCKS5, one auth method: username/password if recv_exact(tcp, 2)[1] != 0x02: raise RuntimeError("username/password authentication is not available") tcp.sendall(b"\x01" + bytes([len(login)]) + login.encode() + bytes([len(password)]) + password.encode()) if recv_exact(tcp, 2)[1] != 0x00: raise RuntimeError("wrong login or password") tcp.sendall(b"\x05\x03\x00\x01" + bytes(6)) # UDP ASSOCIATE, client address 0.0.0.0:0 _, reply, _, atyp = recv_exact(tcp, 4) if reply != 0x00: raise RuntimeError(f"UDP ASSOCIATE failed, reply code {reply}") if atyp == 0x01: relay_ip = socket.inet_ntoa(recv_exact(tcp, 4)) elif atyp == 0x03: relay_ip = recv_exact(tcp, recv_exact(tcp, 1)[0]).decode() else: raise RuntimeError("IPv6 relay address is not handled in this example") relay_port = struct.unpack("!H", recv_exact(tcp, 2))[0] if relay_ip == "0.0.0.0": relay_ip = tcp.getpeername()[0] return tcp, (relay_ip, relay_port) def strip_udp_header(datagram): """Remove the SOCKS5 UDP header from a datagram received from the relay.""" atyp = datagram[3] if atyp == 0x01: # IPv4 return datagram[10:] if atyp == 0x04: # IPv6 return datagram[22:] return datagram[7 + datagram[4]:] # domain name if __name__ == "__main__": tcp, relay = udp_associate(PROXY_HOST, PROXY_PORT, LOGIN, PASSWORD) print("UDP relay:", f"{relay[0]}:{relay[1]}") # DNS query "myip.opendns.com, type A" without EDNS: the answer ends with the IPv4 address query = (os.urandom(2) + b"\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" + b"\x04myip\x07opendns\x03com\x00" + b"\x00\x01\x00\x01") # every datagram to the relay starts with a SOCKS5 UDP header: RSV, FRAG, ATYP=IPv4, destination header = b"\x00\x00\x00\x01" + socket.inet_aton("208.67.222.222") + struct.pack("!H", 53) udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp.settimeout(10) udp.sendto(header + query, relay) answer = strip_udp_header(udp.recv(4096)) print("Exit IP over UDP:", socket.inet_ntoa(answer[-4:])) udp.close() tcp.close()Example output:
UDP relay: 103.152.136.28:39631 Exit IP over UDP: 203.0.113.45Send an HTTP/3 request
For HTTP/3, the QUIC packets of the request go through the same UDP relay. This example uses the aioquic library (Python 3.11 or newer). It loads https://cloudflare-quic.com/cdn-cgi/trace, a plain-text page that shows the protocol and the client IP.
Install the dependencies, save the script as
http3_via_socks5.pynext tosocks5_udp.py, and run it:pip install aioquic certifi python http3_via_socks5.py"""HTTP/3 (QUIC) request to a website through a residential proxy. QUIC packets go to the SOCKS5 UDP relay; the website sees an HTTP/3 connection from the exit IP. Requires: pip install aioquic certifi, and socks5_udp.py from the previous example in the same folder. """ import socket import struct import time import certifi from aioquic.h3.connection import H3_ALPN, H3Connection from aioquic.h3.events import DataReceived, HeadersReceived from aioquic.quic.configuration import QuicConfiguration from aioquic.quic.connection import QuicConnection from aioquic.quic.events import ConnectionTerminated, HandshakeCompleted from socks5_udp import LOGIN, PASSWORD, PROXY_HOST, PROXY_PORT, strip_udp_header, udp_associate TARGET_HOST = "cloudflare-quic.com" TARGET_PATH = "/cdn-cgi/trace" # plain text: shows the protocol (http=http/3) and the client IP tcp, relay = udp_associate(PROXY_HOST, PROXY_PORT, LOGIN, PASSWORD) udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # the destination goes into the SOCKS5 UDP header as a domain name (ATYP=3): the proxy resolves it header = b"\x00\x00\x00\x03" + bytes([len(TARGET_HOST)]) + TARGET_HOST.encode() + struct.pack("!H", 443) config = QuicConfiguration(is_client=True, alpn_protocols=H3_ALPN, server_name=TARGET_HOST) config.load_verify_locations(certifi.where()) quic = QuicConnection(configuration=config) peer = ("0.0.0.0", 443) # placeholder: only the proxy knows the real address quic.connect(peer, now=time.time()) h3 = H3Connection(quic) def send_pending(): for datagram, _ in quic.datagrams_to_send(now=time.time()): udp.sendto(header + datagram, relay) send_pending() stream_id, body, done = None, b"", False deadline = time.time() + 20 while not done and time.time() < deadline: timer = quic.get_timer() udp.settimeout(0.5 if timer is None else min(0.5, max(0.01, timer - time.time()))) try: quic.receive_datagram(strip_udp_header(udp.recv(65535)), peer, now=time.time()) except socket.timeout: if timer is not None and time.time() >= timer: quic.handle_timer(now=time.time()) event = quic.next_event() while event is not None: if isinstance(event, HandshakeCompleted): stream_id = quic.get_next_available_stream_id() h3.send_headers(stream_id, [ (b":method", b"GET"), (b":scheme", b"https"), (b":authority", TARGET_HOST.encode()), (b":path", TARGET_PATH.encode()), ], end_stream=True) elif isinstance(event, ConnectionTerminated): done = True for h3_event in h3.handle_event(event): if isinstance(h3_event, (HeadersReceived, DataReceived)) and h3_event.stream_id == stream_id: if isinstance(h3_event, DataReceived): body += h3_event.data done = done or h3_event.stream_ended event = quic.next_event() send_pending() print(body.decode() or "No HTTP/3 response within 20 seconds") quic.close() send_pending() udp.close() tcp.close()Output (shortened):
ip=203.0.113.45 http=http/3 loc=GB tls=TLSv1.3http=http/3means the website received the request over HTTP/3.ipandlocare the proxy's exit IP and its country.
HTTP/3 in browsers and other apps
An app can send UDP through a proxy only if it supports UDP over SOCKS5 (UDP ASSOCIATE).
Chrome and Firefox do not send UDP through a SOCKS5 proxy set in their own settings. Websites then open over HTTP/2 or HTTP/1.1 instead of HTTP/3.
To use HTTP/3 in a browser or another app, route its traffic through a tool that forwards UDP over SOCKS5. The app never sees the proxy, so it uses HTTP/3 as usual, and the tool sends the QUIC packets through the gateway.
Windows, macOS, Linux. Add a socks outbound with your list details and a tun inbound to capture the traffic of your apps. UDP is enabled in the socks outbound by default:
{
"type": "socks",
"tag": "residential",
"server": "res.proxy-seller.com",
"server_port": 10000,
"version": "5",
"username": "LOGIN",
"password": "PASSWORD"
}Windows. Routes the traffic of selected apps through SOCKS5, including UDP and QUIC. Pick the apps that should go through the proxy and point the profile at the same host, port, login and password.
Limitations
UDP works over SOCKS5 only. HTTP proxy connections carry TCP.
Connecting to the proxy itself over HTTPS (TLS) or HTTP/3 is not supported β use
http://orsocks5://.A UDP session can get a different exit IP than TCP connections on the same port.
FAQ
No. UDP and HTTP/3 work on every list, with the login and password you already have.
No. HTTP and SOCKS5 over TCP keep working exactly as before.
The one with the lowest latency from your server. The gateway does not change the exit IP or its location β those come from the list settings.
Make sure your firewall or hosting provider does not block outgoing UDP to the gateway on ports 30000β39999. And keep the SOCKS5 TCP connection open while you send UDP: the relay closes together with it.
A new list takes 1β2 minutes to activate. Until then the proxy may answer with error 407. Wait a moment and try again.