Proxy-seller

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:

  1. Connect and authenticate

    Your app opens a SOCKS5 connection to the gateway and logs in with the login and password of your list.

  2. Request a UDP relay

    It sends a UDP ASSOCIATE request (RFC 1928). The gateway answers with a relay address: the gateway IP and a port in the 30000–39999 range.

  3. 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

res.proxy-seller.com (Europe), us.res.proxy-seller.com (USA), asia.res.proxy-seller.com (Asia)

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]:10000

The 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

  1. Check the connection

    Any SOCKS5 client will do β€” curl is enough:

    curl -x socks5h://LOGIN:[email protected]:10000 https://api.ipify.org

    The response is the proxy's exit IP. With socks5h://, domain names are resolved by the proxy, not by your machine.

  2. 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.com with 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 run python 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.45
  3. Send 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.py next to socks5_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.3

    http=http/3 means the website received the request over HTTP/3. ip and loc are 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:// or socks5://.

  • 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.

On this page