Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 

Repository files navigation

bypass DPI on firewalls

notes from building a personal tunnel that survives DPI and still feels good enough for FPS games. not a product writeup. the goal was low jitter, not "any connection that works."

files in this repo:

  • setup.sh — server install path (Nginx + Xray, VLESS+WS+TLS, ubuntu-oriented)
  • winInstallation — windows client with v2rayN
  • advanced-setup/ — latency notes / later findings

read the sections in order if you care about why the stack looks the way it does. if you only want install steps, jump to the scripts.


1. problem

i was behind a network that did more than block a few ports. UDP was restricted. web filtering was aggressive. normal VPNs either failed hard or "worked" with enough jitter that games felt awful.

DPI (deep packet inspection) is the usual name for this class of middlebox. it looks at early packets, classifies the flow, then allows, drops, throttles, or resets it. sophos-class gear is one example of the appliance side; the exact brand matters less than the checks it runs.

what i needed:

  • a path that still works when classic VPN protocols are blocked
  • low enough latency and jitter for FPS
  • something i could run myself on a nearby VPS (i used digitalocean-style boxes; a 2 vCPU / 2 GB / ~2 TB transfer plan was enough for a small number of users)

getting "online" is easy. getting a tunnel that does not feel like lag soup is the actual problem.


2. why conventional VPNs fail

firewalls do not have to decrypt you to block you. they need a reliable classifier.

IPsec / L2TP / IKEv2 — old enterprise remote-access stack. very recognizable on the wire. a lot of campus and corporate profiles still have explicit signatures for this. i saw this class of VPN die first.

OpenVPN — still common. handshake patterns and fixed ports make it easy to spot unless you put real work into obfuscation. even then, destination IPs of known providers get blocked.

WireGuard — this is what most modern consumer VPNs actually use, not IPsec. people still write "modern VPNs are IPsec" and that is wrong. WireGuard is excellent when the network allows it. on my path it was not useful: UDP was restricted, and WireGuard's fixed-size UDP handshake is easy to fingerprint when UDP is even allowed.

commercial VPN endpoints — protocol stealth does not help if the egress IP is already on a blocklist.

so the failure modes i kept hitting were:

  1. protocol signature match
  2. UDP / non-443 transport policy
  3. destination IP reputation
  4. later: active probing of anything that looked like a self-hosted "weird HTTPS"

anything that still behaves like a VPN on the wire loses here.


3. how DPI identifies VPN traffic

two buckets: passive and active.

passive

  • port / protocol: UDP 51820-ish for WireGuard defaults, ESP, IKE, openvpn ports, etc.
  • length and timing: fixed handshake sizes, regular keepalives, traffic that does not look like a browser session
  • TLS ClientHello (this one matters a lot): the first client TLS message is mostly cleartext. inside it you get:
    • SNI — the hostname you claim you are visiting (e.g. www.chess.com)
    • cipher suite list and order
    • extension set and order (ALPN, supported groups, GREASE, etc.)

those ClientHello fields hash into fingerprints people talk about as JA3 / JA4-style. chrome on windows has one shape. firefox on linux has another. a default go crypto/tls client has a different shape. DPI can just block or throttle the go-shaped one.

active

if passive signals look off, some boxes open their own TLS session to the same IP:port and check:

  • what certificate comes back
  • whether the server behaves like a normal HTTPS origin
  • whether random probes still smell like a proxy

you need an answer for both. only fixing SNI is not enough. only buying a domain is not enough either if the ClientHello still screams "proxy tool."


4. why HTTPS traffic usually survives

HTTPS is TLS on TCP 443. the modern web is built on it. if a network blocks HTTPS as a class, banking, SaaS, updates, and half of campus tooling die. operators usually will not do that.

that is not the same as "firewalls cannot block HTTPS." they can. they do:

  • drop specific SNI values (deny lists)
  • only allow a small SNI allow-list (harsher environments)
  • MITM TLS on managed devices with a corporate root
  • throttle / reset flows that fail fingerprint or reputation checks while leaving normal browser traffic alone

so the useful property of HTTPS is operational: traffic that looks like ordinary browser HTTPS to somewhere boring is expensive to kill without collateral damage. the rest of this note is about sitting in that bucket, and where that still fails.


5. VLESS + WebSocket + TLS

once the requirement is "look like HTTPS," the stack i landed on for the baseline install is VLESS over WebSocket over TLS, terminated on Nginx, with Xray on localhost.

what each piece is for:

  • VLESS — light proxy protocol. UUID identity, not much framing. carries the real traffic once a channel exists.
  • WebSocket — so the path can look like an upgraded HTTP connection and sit behind Nginx on a secret path.
  • TLS — encrypts everything above. on the wire the middlebox sees TCP 443 + TLS.

rough path:

game / browser / whatever
  -> client (v2rayN, clash, sing-box, ...)
     -> VLESS in WebSocket in TLS
        -> TCP 443 through the firewall
           -> Nginx on VPS (TLS cert for your domain, WS upgrade on secret path)
              -> Xray VLESS inbound on 127.0.0.1
                 -> out to the internet

why this combo worked for me as a starting point:

  • 443/TLS matches what most allow-policies already expect
  • a real domain + certbot cert beats raw-IP HTTPS, which some filters treat as sketchy (i used a free duckdns name pointed at the VPS)
  • Nginx can serve something normal on / and only proxy /my-secret-path (or whatever path you pick)
  • VLESS does not add a lot of overhead compared to heavier wrappers

setup.sh and winInstallation document this baseline. it beats "block all IPsec / WireGuard." it does not automatically beat fingerprinting or active probes.


6. TLS fingerprinting

TLS encrypts application data. it does not hide the whole handshake.

ClientHello is visible. ECH (encrypted client hello) exists but is not something you can assume is on everywhere, and a lot of proxy stacks still expose SNI the normal way.

so DPI can:

  1. read SNI and apply a blocklist / allow-list
  2. hash the rest of the ClientHello and compare to known browser vs known tool fingerprints

those are different controls. i used to mash them together in earlier notes. they are not the same.

control what it looks at what actually helps
SNI policy hostname string allowed / unlisted name, or REALITY camouflage name
fingerprint policy ClientHello structure uTLS / browser fingerprint on the client
active probe cert + server behavior real site in front, or REALITY

on my path, SNI was the first filter (blocked sites by name). after VLESS+WS+TLS with my own domain got through that, the remaining pain looked more like throttle/jitter than a hard block. that lines up with fingerprint / behavior based treatment, not "SNI said no."

a default go TLS client does not look like chrome. Xray is go. if you leave the fingerprint alone, you can still lose.


7. uTLS

uTLS is a go TLS library that can rebuild ClientHellos to match real clients (chrome, firefox, safari, ios, etc.).

in practice you set something like:

"fingerprint": "chrome"

in v2rayN / clash / sing-box / whatever client you use. the exact field name depends on the client.

what uTLS does: make the first packets look like a browser starting HTTPS, instead of "stock go crypto/tls."

what it does not do:

  • hide your server certificate identity
  • beat an SNI allow-list that simply does not include your name
  • fix traffic shape / long-lived flow analysis

for the Nginx + VLESS+WS+TLS setup, turning on a chrome (or similar) fingerprint was one of the more useful anti-jitter changes after the tunnel already connected. hard blocks and soft throttles are different failure modes; fingerprinting often shows up as the second one.

more detail: https://github.com/refraction-networking/utls


8. REALITY

even with uTLS and a let's encrypt cert on something like vpn.yourdomain.tld, an active probe can still see:

  • cert is for a random / new name
  • server does not behave like a big public origin

REALITY (in Xray-core; also in sing-box) is the next step people reach for. i treated sing-box / Xray as the engines that implement this; the interesting part is the handshake design, not the brand of the binary.

what people get wrong

a common short explanation is: "REALITY forwards the handshake to microsoft and you ride inside that session." that is too sloppy.

you are not getting free bandwidth through microsoft. your proxy traffic still terminates on your VPS after a successful REALITY auth. microsoft (or whatever site you pick as camouflage) is a reference / dest used to shape what probers see, not your actual application endpoint.

closer mental model

  1. client uses uTLS, puts a high-traffic hostname in SNI (microsoft is a common example in tutorials; it is just a config choice, and some targets break when their TLS details change).
  2. client also carries REALITY auth material in the handshake (session id space, AEAD over an X25519 shared secret, short id, etc. — see XTLS/REALITY and this source walkthrough).
  3. server uses its private key to decide if this is a real client.
  4. for the handshake, the server dials a real dest and uses that exchange to produce ServerHello / cert presentation that matches what a probe expects for that name. for authorized clients, temporary cert / signature material is tied to the REALITY shared key so the client can authenticate the proxy without needing a CA cert for your personal hostname.
  5. unauthorized connections get forwarded toward dest, so a probe that is not a real client still sees something that looks like the real site's TLS behavior instead of a custom dead service.
  6. after auth, VLESS (or whatever you run) rides between client and your server over that TLS channel.

so: cryptographic camouflage + probe handling + client auth. not "tunnel everything through microsoft."

when i would stay on plain VLESS+WS+TLS instead

  • network only kills classic VPN / UDP and does not care about fingerprints or obscure certs
  • you want simpler ops (certbot + Nginx, secret path, done)
  • you want a real website on the same box as the proxy path

9. complete packet flow

baseline (what this repo's install scripts build)

client machine
  app traffic (system proxy or TUN)
    -> VLESS
      -> WebSocket
        -> TLS   <-- ClientHello is visible to DPI here
          -> TCP/443
            -> through firewall
              -> your VPS

on VPS:
  Nginx terminates TLS (your domain cert)
    -> HTTP 101 upgrade on secret path
      -> Xray VLESS on 127.0.0.1
        -> outbound to destination

DPI sees: TCP to your VPS, port 443, ClientHello with your SNI, hopefully a browser-like fingerprint if you set uTLS. after handshake: opaque TLS records.

DPI does not see (without MITM on the client device): VLESS UUID, WS payload, destinations inside the tunnel.

REALITY path

client
  VLESS
    REALITY / TLS 1.3 via uTLS
      SNI = camouflage name
      auth = REALITY public key + short_id material
        -> TCP/443 -> your VPS

server
  if auth ok  -> finish REALITY handshake -> VLESS -> internet
  if auth bad -> forward / mimic via dest so probes look normal

DPI sees TLS to your IP with a popular SNI and a browser-shaped ClientHello. it does not automatically learn "this is VLESS" from cleartext alone.

where each layer applies on the path

flow hits middlebox
  -> SNI policy          (domain / camouflage name)
  -> fingerprint policy  (uTLS)
  -> active probe        (REALITY or a real site on the IP)
  -> traffic analysis    (still hard; long flows can look like proxies anyway)

10. performance

bypass and "FPS feel" are different problems. a tunnel can connect and still feel bad.

what actually moved the needle for me:

  • VPS location — closest region that is still outside the restricted network. RTT is mostly distance and peering. crypto on a modern CPU is rarely the bottleneck for a few users.
  • provider — gaming/streaming burns transfer. some clouds punish that on price; i preferred simpler VPS pricing (digitalocean-class) for that reason.
  • do not stack features you did not measure — VLESS is light; multiplex, extra routing, unnecessary wrappers add buffering.
  • UDP inside TCP — if the firewall kills native UDP, you carry game UDP inside the TCP/WS tunnel. under loss that can feel worse (TCP-over-TCP style pain). low loss on the outer path matters.
  • fingerprint throttle — wrong ClientHello sometimes does not hard-block; it throttles. that showed up as jitter for me more than as "connection failed."
  • local junk — bufferbloat and wifi will fake a DPI problem. wire the client when you debug latency.
  • box size — 2 vCPU / 2 GB was fine for a small group; path RTT dominated.

more on latency workarounds lives under advanced-setup/.


11. limits

things this does not solve:

  • managed-device MITM — if the network owns the client and installs a corporate root, they can intercept HTTPS. different fight.
  • ECH is not a free win — deployment is uneven; many stacks still expose SNI.
  • traffic analysis — long-lived, high-volume, steady duplex flows can still look like a proxy even when the handshake is perfect.
  • IP blocklists — if your VPS IP is burned, camouflage protocols do not unburn it. move or front it.
  • REALITY dest choice — targets need compatible TLS (usually TLS 1.3, parameters that do not break the implementation). popular names sometimes stop working when cert sizes / behavior change. test, have a backup dest.
  • policy / law — bypassing network controls may break local rules. this is research and personal ops notes; you own your use.
  • ops cost — REALITY + key handling is more moving parts than WireGuard on a clean network. use the smallest stack that matches the actual middlebox.

how to use this repo

  1. read 1–4 if you have not already. the install only makes sense with the threat model.
  2. stand up baseline VLESS+WS+TLS with setup.sh on a nearby VPS.
  3. connect from windows with winInstallation. set fingerprint to chrome (or similar) if the client supports it.
  4. if you still get throttle / probe-style failures, look at REALITY via Xray or sing-box instead of only changing ports.
  5. chase latency in advanced-setup/.

links i used


personal research notes. copy configs only after you know which layer of the problem you are actually stuck on.

About

Achieve stable, low-latency connection behind restrictive firewalls. Documents investigation & provides manual setup guides for VLESS+WS+TLS (Nginx/Xray) and discussed other protocols(Hysteria2,IKev2)

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages