SOCKS5 is a widely used proxy protocol for facilitating secure and efficient communication between clients and servers.
Compared to SOCKS4, SOCKS5 adds the following capabilities:
- Authentication
- IPv6
- Domain name resolution (DNS)
- UDP association
HTTP proxies are even more limited — they handle HTTP(S) traffic exclusively.
Note: proxy technologies are not encrypted by default, and most are not capable of encryption at all. Proxying a connection is primarily used to evade censorship or bypass geofencing (IP-based blocking).
- Why use a private proxy instead of a private VPN?
- When encryption is not required, a SOCKS5 proxy has much lower overhead than a VPN. A single-core VM or container can saturate its network interfaces with minimal latency. A proxy is also easier to scope to a single application (e.g. a secondary browser) rather than routing all traffic from the entire OS or network.

Note: my internet connection was the bottleneck during the speed test.
It is worth noting that multiple sources online offer frequently updated lists of open proxy servers [Link].
Proxies can also be used to cache static web content and reduce traffic on metered or impaired links [Link], though that is outside the scope of this post.
The following examples show how to proxy through another server to access the internet while hiding the real source of the request.
GOLANG — A high-performance proxy written in Go using a third-party module [Link]:
Create a new file called main.go and add the following content:
package main
import (
"fmt"
"syscall"
"github.com/things-go/go-socks5"
)
func main() {
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &syscall.Rlimit{Max: 65536, Cur: 65536}); err != nil {
fmt.Println("Error setting rlimit:", err)
}
server := socks5.NewServer()
if err := server.ListenAndServe("tcp", ":1080"); err != nil {
panic(err)
}
}
go run main.go
Note: syscall is used here to raise the open file descriptor (connection) limit for the process. The same result can be achieved from the console with ulimit -n 65536.
PYTHON — This example requires a username and password for authentication. It is a fork of the code available at [Link].
import select
import socket
import struct
from socketserver import ForkingTCPServer, TCPServer, StreamRequestHandler
class ThreadingTCPServer(ForkingTCPServer, TCPServer):
pass
class SocksProxy(StreamRequestHandler):
username = 'username'
password = 'password'
def handle(self):
# greeting header - read and unpack 2 bytes from a client
header = self.connection.recv(2)
version, nmethods = struct.unpack("!BB", header)
# socks 5
assert version == 5
assert nmethods >= 0
# get available methods
methods = self.get_available_methods(nmethods)
# accept only USERNAME/PASSWORD auth
if 2 not in set(methods):
# close connection
self.server.close_request(self.request)
return
# send welcome message
self.connection.sendall(struct.pack("!BB", 5, 2))
if not self.verify_credentials():
return
# parsing the request
version, cmd, _, address_type = struct.unpack("!BBBB", self.connection.recv(4))
assert version == 5
if address_type == 1: # IPv4
address = socket.inet_ntoa(self.connection.recv(4))
elif address_type == 3: # DNS
try:
domain_length = self.connection.recv(1)[0]
address = self.connection.recv(domain_length)
address = socket.gethostbyname(address)
except:
address = '0.0.0.0'
port = struct.unpack('!H', self.connection.recv(2))[0]
# replying to the request
try:
if cmd == 1:
remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote.connect((address, port))
bind_address = remote.getsockname()
else:
self.server.close_request(self.request)
addr = struct.unpack("!I", socket.inet_aton(bind_address[0]))[0]
port = bind_address[1]
reply = struct.pack("!BBBBIH", 5, 0, 0, 1,
addr, port)
except Exception as err:
reply = self.generate_failed_reply(address_type, 5)
self.connection.sendall(reply)
# establish data exchange
if reply[1] == 0 and cmd == 1:
self.exchange_loop(self.connection, remote)
self.server.close_request(self.request)
def get_available_methods(self, n):
methods = []
for i in range(n):
methods.append(ord(self.connection.recv(1)))
return methods
def verify_credentials(self):
piece_of_data = self.connection.recv(1)
if len(piece_of_data) == 0:
return
version = ord(piece_of_data)
assert version == 1
username_len = ord(self.connection.recv(1))
username = self.connection.recv(username_len).decode('utf-8')
password_len = ord(self.connection.recv(1))
password = self.connection.recv(password_len).decode('utf-8')
# set the following flag to True to skip credential checks
accept_any_creds = False
if (username == self.username and password == self.password) or accept_any_creds:
response = struct.pack("!BB", version, 0)
self.connection.sendall(response)
return True
response = struct.pack("!BB", version, 0xFF)
self.connection.sendall(response)
self.server.close_request(self.request)
return False
def generate_failed_reply(self, address_type, error_number):
return struct.pack("!BBBBIH", 5, error_number, 0, address_type, 0, 0)
def exchange_loop(self, client, remote):
while True:
try:
# wait until client or remote is available for read
r, w, e = select.select([client, remote], [], [])
if client in r:
data = client.recv(4096)
if remote.send(data) <= 0:
break
if remote in r:
data = remote.recv(4096)
if client.send(data) <= 0:
break
except KeyboardInterrupt:
self.server.close_request(self.request)
exit()
except Exception as err:
print(err)
if __name__ == '__main__':
with ThreadingTCPServer(('0.0.0.0', 1080), SocksProxy) as server:
try:
server.serve_forever()
except:
pass
BONUS
The following PHP script can be dropped onto an existing web server to provide a simple single-request proxy:
<?php
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$url = $_GET['url'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, getallheaders());
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $header_size);
$body = substr($response, $header_size);
curl_close($ch);
foreach (explode("\r\n", $header) as $header_line) {
header($header_line);
}
echo $body;
} else {
die('Invalid request method');
}
?>
It requires the PHP cURL module:
sudo apt-get install php-curl -y sudo systemctl restart apache2
Usage (file size is limited by the PHP memory limit set in the server configuration):
curl "http://200.200.200.200/path/proxy.php?url=https://7-zip.org/a/7z2201-x64.exe" > 7zip.exe