Why Build a Custom Proxy Server
Custom proxy servers give you control over routing logic, authentication, logging, and protocol handling that off-the-shelf solutions don't provide. Use cases: building a proxy service (like ProxifyPRO), implementing custom auth schemes, routing requests through specific network interfaces (each 4G dongle as separate egress), and educational purposes.
The Three Proxy Protocols
| Protocol | Layer | HTTPS Support | Authentication | Common Port |
|---|---|---|---|---|
| HTTP | Application | Via CONNECT | Proxy-Authorization header | 8080, 3128 |
| HTTPS | Application + TLS | Native | Proxy-Authorization header | 8443 |
| SOCKS5 | Transport | Native (TCP pipe) | Username/password auth | 1080 |
SOCKS5 is more versatile — it proxies any TCP/UDP traffic, not just HTTP. Most scraping libraries support both HTTP and SOCKS5 interchangeably.
HTTP Proxy Mechanics
For plain HTTP requests, the proxy receives the full HTTP request from the client (with absolute URL), forwards it to the destination, and returns the response:
# Client sends: GET http://example.com/page HTTP/1.1 Host: example.com Proxy-Authorization: Basic dXNlcjpwYXNz # Proxy strips Proxy-Authorization, forwards: GET /page HTTP/1.1 Host: example.com # Returns response to client
Key difference from a regular request: the URL is absolute (includes scheme and host), not relative. Proxy must parse the host from the URL and make a new connection to the upstream.
HTTPS CONNECT Tunnel
HTTPS uses the CONNECT method — the client asks the proxy to establish a raw TCP pipe to the destination. The proxy then transparently forwards bytes in both directions without decrypting.
# Client sends CONNECT request: CONNECT example.com:443 HTTP/1.1 Host: example.com:443 Proxy-Authorization: Basic dXNlcjpwYXNz # Proxy responds (tunnel established): HTTP/1.1 200 Connection established # Then: raw TLS bytes flow bidirectionally # Proxy is transparent - doesn't see encrypted content
Implementing CONNECT in Node.js
const net = require('net')
const http = require('http')
const proxy = http.createServer()
proxy.on('connect', (req, clientSocket, head) => {
const [host, port] = req.url.split(':')
// Verify auth (Proxy-Authorization header)
if (!verifyAuth(req.headers['proxy-authorization'])) {
clientSocket.write('HTTP/1.1 407 Proxy Auth Required
')
return clientSocket.destroy()
}
// Connect to upstream
const serverSocket = net.connect(port || 443, host, () => {
clientSocket.write('HTTP/1.1 200 Connection established
')
serverSocket.write(head)
// Bidirectional pipe
serverSocket.pipe(clientSocket)
clientSocket.pipe(serverSocket)
})
serverSocket.on('error', () => clientSocket.destroy())
})
proxy.listen(8080)
SOCKS5 Protocol
SOCKS5 operates at the transport layer. Handshake: client sends greeting (methods) → server selects auth method → client authenticates → client sends CONNECT/BIND/UDP request → server establishes upstream connection → bidirectional data flows.
SOCKS5 advantages over HTTP proxy: supports UDP (HTTP CONNECT is TCP only), no HTTP overhead, simpler protocol, no URL-based filtering (privacy benefit), supports IPv6 natively.
Production Challenges
Concurrency
Each proxy connection needs: 2 TCP connections (client + upstream), 2 buffers (~64KB each), event loop registration. At 1000 concurrent connections = 1000 upstream connections, 128MB buffers. Node.js handles this well with async I/O. Python threading doesn't scale — use asyncio.
Per-Interface Routing
ProxifyPRO's key feature: each dongle uses a different network interface. Proxy traffic through dongle 1 must egress via wwan0, dongle 2 via wwan1. This requires binding outgoing sockets to specific interfaces:
# Policy routing per dongle interface (Linux) ip route add default via 192.168.1.1 dev wwan0 table 101 ip rule add fwmark 1 table 101 # iptables marks traffic from proxy port 40001 → table 101 iptables -t mangle -A OUTPUT -p tcp --sport 40001 -j MARK --set-mark 1
Framework Comparison
| Framework | Language | Perf | Ease | Best For |
|---|---|---|---|---|
| 3proxy | C | Excellent | Medium | Production proxy engine |
| Squid | C | Excellent | Hard | Caching proxy, enterprise |
| mitmproxy | Python | Medium | Easy | Interception, analysis |
| Caddy | Go | Very Good | Easy | TLS-forward proxy, modern |
| Node.js custom | JS | Good | Easy | Custom logic, control |
3proxy: ProxifyPRO's Engine
ProxifyPRO uses 3proxy as its proxy engine — a lightweight (~50KB binary), battle-tested C proxy server that supports HTTP, HTTPS, SOCKS5, and per-interface binding. 3proxy handles thousands of concurrent connections on minimal CPU/RAM.
ProxifyPRO's Node.js layer handles: dongle management, authentication (Keygen license validation), health monitoring, rotation, SMS handling, dashboard, and marketplace. 3proxy handles: the actual TCP proxying. This separation gives C-level proxy performance with Node.js-level business logic flexibility.
Building a proxy from scratch teaches the protocol. For production use, 3proxy + orchestration layer (Node.js/Python for business logic) is the proven architecture. ProxifyPRO has done this work — the Starter license gives you the complete production stack for $60/month.