SNIProxy, a hostname-aware TCP forwarder for load balancing your own servers
🧷 自用的简单 SNIProxy(常用于网站负载均衡、基于域名(SNI)的端口转发等
At a glance
- What is it?
- SNIProxy reads the SNI hostname out of a TLS ClientHello and forwards the connection to that domain's origin server, which lets several servers sit behind one address and share traffic. The author is explicit that it is a personal tool, not production software, and that it does nothing to hide the hostname from anyone on the path.
- Who is it for?
- SNIProxy suits an operator who has several servers behind one address and wants traffic to reach whichever origin a client's hostname points at, without terminating TLS. It does not suit a production edge, because the author says so plainly, and it does not suit anyone expecting a general-purpose load balancer.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 180 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A port forwarder that reads the hostname
SNIProxy is a small personal tool that the author shares for people with the same need. It is a proxy that forwards data to the origin server of whichever domain the client asked for, and it is commonly used for load balancing a website across several servers.
The mechanism is stated in one sentence in the README, and it is the whole design. SNIProxy forwards data to the origin server of the domain passed in, and because the target domain is obtained from the cleartext SNI, there is no need to decrypt and re-encrypt SSL, which the author says makes forwarding faster and more efficient.
So this is a TCP-level proxy that inspects the first packet of a connection, reads the SNI field of the TLS handshake, resolves that hostname, and pipes the connection through to the resolved address on port 443. The bytes after the handshake are not read at all. That is why it is cheap: it is not a terminating proxy, it is a sniffer and a pipe.
The comparison the author draws with ordinary port forwarding is the point. A port forward can only point at one fixed target IP, while SNIProxy gets the target IP from DNS resolution of the incoming hostname, so different hostnames go to different origins, and they can exist at the same time without interfering.
The consequence is that several servers can share one address. The author describes pointing the DNS for a domain at several of these servers so that visitors are assigned to one of them, which gives load balancing and traffic distribution. And the same mechanism allows DNS zone routing to send visitors from different regions or carriers to a server that is closer to them, which is a form of anycast you implement in DNS.
The project is written in Go, which the feature list credits for making it work on all platforms and all operating systems. The licence is GPL-3.0, and the recorded releases are v1.0.7 from 2026-04-03, which added a target address check, v1.0.6 from 2025-06-19, which added support for a username and password on the proxy configuration, and v1.0.5 from 2025-06-16, which added listening on HTTP and automatically redirecting to HTTPS. The last push to the repository was on 2026-04-26.
What the project says it is not for
The README has an important callout, and it is worth reading closely because it removes an obvious use case that the tool's mechanics might otherwise suggest.
The note explains that SNIProxy only does port forwarding and load balancing. All the data it receives is forwarded to the target origin server unchanged, including the cleartext SNI domain information, which any third party such as a network filter can still see directly. Therefore SNIProxy cannot be used to circumvent a network block, and the author's reasoning is that it would be pointless, because such a network can already block by domain name.
That is a precise piece of security thinking, and it is the difference between this tool and the category of software that people often reach for when they want to reach a blocked site. SNIProxy does not hide anything. The client sends a ClientHello with the hostname in the clear, the proxy reads it, and the connection is forwarded verbatim. Anyone on the path sees the same hostname the client did, so there is no censorship-resistance property here at all.
There is a second warning in the same spirit, and it is about reliability. The author states that SNIProxy was written for personal use only and that its reliability and stability are not as good as professional commercial software such as Nginx or HAProxy, and that it is not recommended for use in a formal production environment, and that under the GPL-3.0 licence the project accepts no responsibility for any loss caused by using it.
That is a candid statement from the person who wrote the code, and it should be taken at face value rather than as marketing modesty. The project is a few hundred lines of Go, written by one person to solve one problem. The README says as much, describing the code as very compact because it was written for personal convenience while learning.
So the correct framing is this: it is a good tool for a specific, narrow and legitimate job, by someone who knows exactly where its limits are. The use case is running several of your own servers behind one address, and the author is telling you not to put it in front of anything whose uptime you care about.
The four-step data path
The workflow is described in four steps, and it is short enough to hold in your head.
First, it parses the TLS or SSL handshake message in the incoming connection, in cleartext, to obtain the SNI domain the visitor sent. Second, it checks whether that domain is on the allowlist, or whether `allow_all_hosts` is enabled, and if it is not, the connection is broken. Otherwise it continues. Third, it uses system DNS to resolve the SNI domain and get the IP address of that domain's origin server. Fourth, it forwards the received data unchanged to that origin server's IP on port 443, establishing a bridge between the visitor and the origin for continuous two-way data transfer.
The step worth pausing on is the third. The target address is not in the configuration, it comes from a DNS lookup performed by the proxy at connection time. That is what makes the tool flexible, and it is also its main operational dependency: the proxy's resolver has to give the right answer, and a misconfigured or stale resolver turns into connections to the wrong place.
The later release, v1.0.7, added a target address check, which reads as a response to exactly that failure mode. The repository top level includes `config.yaml` and the Go sources are `main.go`, `common.go` and `interfaces.go`, which is a small enough tree to read in one sitting.
The forwarding to port 443 specifically is worth noting. This is a TLS-only design in practice, because the SNI only exists in a TLS handshake. A plaintext HTTP request has no SNI, which is why the v1.0.5 release added the ability to listen on HTTP and redirect to HTTPS rather than trying to forward plaintext on the basis of a Host header.
The Socks5 support extends the chain in the other direction. The feature list says SNIProxy supports a Socks5 upstream proxy, with the example given being that you could put a layer of WARP in front so that SNIProxy's own exit address becomes a Cloudflare one. The README draws the path as the visitor going to SNIProxy, then to the Socks5 proxy, which does the SNI resolution, then to the origin server. The v1.0.6 release added support for a username and password on that proxy configuration, which is what you need when the upstream proxy requires credentials.
What it supports, and the one protocol it will not
The feature list is short and each item is a switch in the configuration.
It supports all platforms and all operating systems, which follows from the Go implementation and the release archives. It supports a Socks5 upstream proxy. It supports allowing all domains or allowing only specified domains, where a specified domain includes itself and all of its subdomains. It supports listening and carrying IPv4 and IPv6 traffic separately or together. It supports redirecting HTTP to HTTPS. It supports the HTTP/1.1 and HTTP/2 transport protocols. And it supports forwarding based on the cleartext SNI to the domain's origin server, with no encryption or decryption of the traffic and no need for an SSL key or certificate.
That last point is the one that makes the tool unusual. A conventional reverse proxy terminates TLS, which means it holds a certificate for your domain and can inspect, rewrite and log the traffic inside the encryption. SNIProxy never terminates anything, so it needs no certificate at all, and the origin server sees the client's own TLS session end to end.
The HTTP/2 support has a subtlety the README handles well, and it is worth understanding because it looks like it should break the design. HTTP/2 can multiplex several domains over one TCP connection, which at first glance means SNIProxy would see one connection carrying traffic for several hostnames and would not know where to forward it. The explanation is that modern browsers only reuse a connection when several conditions all hold, namely that the domains resolve to the same IP, that they use the same TLS certificate, such as a wildcard certificate or one covering several names, and that they use the same port such as 443. When those conditions hold, the SNI is the same anyway, so there is nothing to disambiguate, and the program just forwards the traffic and lets the browser and the server negotiate.
The one protocol it does not support is HTTP/3 with QUIC, and the reason is structural. QUIC uses UDP rather than TCP, and reading the SNI from a QUIC handshake would need a different implementation. The author says the tool does not support UDP and does not intend to add it, because it would make the project much more complicated, and notes that the code was written for personal convenience while learning and is only a few hundred lines.
That last constraint has a practical consequence. If your origin server offers HTTP/3 to clients and a browser negotiates it by default, a client connecting through SNIProxy will not be able to use QUIC, because the proxy is TCP only. The client will normally fall back to TCP, but a site that has been configured to require HTTP/3, or a client that has QUIC hard-coded, will not work through this proxy at all.
Running it, and the two things the README tells you to prepare
The installation path is a release archive and a configuration file, and the documentation covers Linux, Windows and macOS with the same shape.
On Linux, the sequence is to create a folder, download the archive, extract it, make the binary executable, edit the configuration and run it.
wget -N https://github.com/XIU2/SNIProxy/releases/latest/download/sniproxy_linux_amd64.tar.gztar -zxf sniproxy_linux_amd64.tar.gzchmod +x sniproxy./sniproxy -c "config.yaml"The download URL always points at the latest release, and the README notes that the file name can be replaced if you want a specific version, and that the release page lists the available versions and file names. There is also a set of mirror URLs for users on Chinese networks, which is a practical detail for the audience the author is writing for.
The configuration is a YAML file named `config.yaml`, edited with whatever text editor is to hand, and the README links to a section explaining its contents.
The command line is minimal. The `-c` flag names the configuration file, with a default of `config.yaml`. The `-l` flag names a log file, with no default. The `-d` flag turns on debug mode, which is off by default. Running the binary with no arguments at all prints the version, the project URL and the list of options, so the help output is also the quickest way to check what a given build supports.
Two operational recommendations are worth following. The first is to raise the system file handle limit, because the tool holds a file descriptor per connection and will otherwise fail with a too many open files error under load. The second is to register it as a system service, so that it starts on boot, restarts after an unexpected exit, runs in the background and is manageable, with a section in the README for configuring it under systemd on Linux.
That second point is not a detail of convenience. The author's own warning about reliability is exactly the kind of thing a supervisor is for. A process that dies on a transient error and does not come back is the difference between a load balancer and an outage, and a systemd unit with restart policy is the minimum you would want for anything in front of a real site, even a small one.
On Windows the instructions are to download the executable from the releases, edit `config.yaml` with a text editor and run `sniproxy.exe`, either by double-clicking it or from a command prompt with the `-c` flag.
The codebase, and how it compares to the alternatives
The dependency list is a good proxy for how small this is.
module github.com/XIU2/SNIProxy
go 1.18
require (
golang.org/x/net v0.0.0-20220812174116-3211cb980234
gopkg.in/yaml.v2 v2.4.0
)Two dependencies. `golang.org/x/net` for the TLS handling, which is where the SNI parsing comes from, and `yaml.v2` for the configuration file. The Go directive is 1.18, and the top-level entries are `main.go`, `common.go`, `interfaces.go`, `config.yaml`, `go.mod` and `go.sum`.
A `go.sum` that short, a source tree that is four files, and a codebase the author describes as a few hundred lines. That is the shape of a tool you can read end to end in an evening, which is the strongest argument for it if you are the kind of person who wants to know exactly what is in front of your traffic.
The alternatives are the serious ones. Nginx and HAProxy are what the author tells you to use in production, and they are what most people should use. The difference in approach is that both of those can terminate TLS, which means they need a certificate, they can route on more than the SNI, they can rewrite headers, they can cache, they can log the decrypted request, and they have been hardened against a decade of attacks. SNIProxy does none of that, deliberately, because it is trying to be a pipe and not a proxy in the general sense.
There is also a third category worth naming, the layer 4 load balancer sold as an appliance or as a hosted service, which balances on the connection rather than on the request and never sees the payload either. SNIProxy is a very small, very specific member of that category, with the distinguishing feature that it picks the destination from the hostname the client asked for rather than from a configured pool.
That feature is the reason to use it at all. If you run the same site on several servers and want one address to serve them all with the client reaching the right one, a SNI-aware forwarder does that with no certificate management and no configuration of upstream pools. If you do not need that, the professional tools are better and the author says so.
Editorial conclusion
SNIProxy suits an operator who has several servers behind one address and wants traffic to reach whichever origin a client's hostname points at, without terminating TLS. It does not suit a production edge, because the author says so plainly, and it does not suit anyone expecting a general-purpose load balancer. Verify first that you can raise the system file handle limit, since the tool is connection-per-connection and will otherwise fail with too many open files, that your hostnames are in the allowlist or that allow_all_hosts is on, and that you accept that the SNI hostname travels in cleartext to every hop, which is why the project is not a way around a network that blocks by domain.
Frequently asked questions
What is SNIProxy?
It is a small Go program that reads the SNI hostname out of a TLS handshake and forwards the connection to that domain's origin server, resolving the target address with system DNS. Because it never decrypts the traffic, it needs no SSL certificate, and it is commonly used to load balance one domain across several servers.
Can SNIProxy be used to reach blocked sites?
No, and the project says so explicitly. All data is forwarded unchanged, including the cleartext SNI hostname, which any third party on the path can still see, so there is no censorship-resistance property. The author's point is that a network able to block by domain name can already do that.
How do I run SNIProxy?
Download the release archive for your platform, extract it, make the binary executable, edit config.yaml and run it. The only command line options are -c for the configuration file, -l for a log file and -d for debug mode, and running it with no arguments prints the help and the version.
What does SNIProxy support and what does it not?
It supports all platforms through the Go implementation, a Socks5 upstream proxy, allowing all domains or an allowlist including subdomains, IPv4 and IPv6, redirecting HTTP to HTTPS, and the HTTP/1.1 and HTTP/2 protocols. It does not support HTTP/3 with QUIC, because that runs over UDP, and the project says UDP support is not planned.
Is SNIProxy suitable for production use?
The author states that it was written for personal use, that its reliability and stability are not as good as professional software such as Nginx or HAProxy, and that it is not recommended for a formal production environment. The README also recommends raising the system file handle limit and registering the program as a system service so it restarts after a failure.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/xiu2-sniproxy)Was this useful?