HAProxy
SNI proxy
frontend https
    bind :443
    mode tcp 
    acl clienthello req.ssl_hello_type 1
    acl site1 req.ssl_sni -i domain1.com
    acl site1 req.ssl_sni -i -m end .domain1.com
    acl site2 req.ssl_sni -i domain2.org
    acl site2 req.ssl_sni -i -m end .domain2.org
    tcp-request inspect-delay 2s
    tcp-request content accept if clienthello
    use_backend web-https-site1 if site1
    use_backend web-https-site2 if site1
    default_backend default-site
Pass client IP to backend server
Client ---> HAProxy --(connects with client IP)--> Backend
       <---         <---(responds to client IP)---
The idea is that HAProxy connects to the backend server on a loopback address with the IP address of the client, and catches response with the help of iptables and policy routing.
Note
Preferably, the backend server should bind to an address that no one else can reach. Loopback addresses, namely addresses in 127/8, is avoided, because to use them for routing purposes one needs to turn on sysctl route_localnet, which has security implications. We can still create a dummy interface and assign a reserved address to it to avoid route_localnet.
In the following example, the address of the dummy interface is 203.0.113.1.
/etc/haproxy/haproxy.cfg:
frontend http
    bind :80 transparent
    mode tcp 
    option tcplog
    default_backend web-http
backend web-http
    mode tcp 
    source 0.0.0.0 usesrc clientip
    server nginx 203.0.113.1:81
iptables:
-t mangle -N TRANSPROXY
-t mangle -A OUTPUT -p tcp --sport 81 -j TRANSPROXY
-t mangle -A TRANSPROXY -j CONNMARK --restore-mark
-t mangle -A TRANSPROXY -m mark ! --mark 0 -j ACCEPT
-t mangle -A TRANSPROXY -j MARK --set-mark 1
-t mangle -A TRANSPROXY -j CONNMARK --save-mark
ip rule:
ip rule add fwmark 0x1 lookup 100
ip route add local default dev lo