The university Wi-Fi blocks gaming. Not just specific ports they use Deep Packet Inspection (DPI) to analyze traffic patterns and kill anything that looks like a game, a VPN, or anything else they don't approve of. They can't block port 443 (HTTPS) because that would break the entire internet, so they inspect what's flowing through it and selectively throttle or drop connections that don't look like "real" web browsing.

Challenge accepted.

What Didn't Work

Attempt 1: OpenVPN over TCP 443

In the previous chapter, I set up sslh to multiplex HTTPS and OpenVPN on port 443. The idea was that OpenVPN over TCP 443 would look like HTTPS to the university firewall.

It didn't. Connected fine, but zero internet through the tunnel. Even curl https://example.com failed. The university's DPI can tell the difference between real HTTPS and OpenVPN-over-TCP by looking at packet patterns, timing, and handshake signatures. OpenVPN has a distinctive fingerprint even on port 443.

Quick test confirmed it: same VPN config worked perfectly on my phone's mobile hotspot. The bottleneck was specifically the university network.

Attempt 2: stunnel (TLS-wrapped OpenVPN)

The next idea: wrap OpenVPN in a real TLS layer using stunnel. This makes the traffic truly indistinguishable from HTTPS at the protocol level, real TLS handshake, real cipher negotiation, everything.

Set up stunnel on both server and client, configured sslh with SNI-based routing to direct traffic to the right service... and it didn't work. The chain had too many moving parts: sslh โ†’ stunnel โ†’ OpenVPN, each with its own config, certificates, and failure modes. After hours of debugging permission issues and TLS handshake failures, I gave up and reverted everything.

Sometimes the "proper" solution is too complex for its own good.

What Worked: Xray (V2Ray)

Then I discovered Xray, the actively maintained fork of V2Ray. Originally designed to bypass China's Great Firewall which is arguably the most sophisticated DPI system in the world. If it can beat the GFW, it can beat a university Wi-Fi filter.

The approach is fundamentally different from OpenVPN or stunnel. Instead of wrapping VPN traffic in TLS, Xray uses the VLESS protocol over WebSocket, served through Nginx on port 443. To any DPI system, the traffic looks exactly like someone browsing a normal HTTPS website because it literally IS a WebSocket connection to a real HTTPS website. My website. With a real TLS certificate. Served by real Nginx.

"The best disguise isn't pretending to be something else, it's actually being something else."

Server Setup

Installed Xray on the server:

sudo bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install

Generated a UUID (acts as the authentication key):

xray uuid

Configured Xray to listen on a local port using the VLESS protocol over WebSocket:

{
  "inbounds": [{
    "port": 10000,
    "listen": "127.0.0.1",
    "protocol": "vless",
    "settings": {
      "clients": [{"id": "[UUID]", "level": 0}],
      "decryption": "none"
    },
    "streamSettings": {
      "network": "ws",
      "wsSettings": {"path": "/v2ray"}
    }
  }],
  "outbounds": [{
    "protocol": "freedom",
    "settings": {},
    "tag": "direct"
  }],
  "routing": {
    "rules": [{
      "type": "field",
      "ip": ["geoip:private"],
      "outboundTag": "direct"
    }]
  }
}

The routing section is important: it tells Xray to route traffic to private IP ranges (like 192.168.x.x) directly, so I can also SSH into the server through the tunnel.

Nginx WebSocket Proxy

Added a location block to my existing Nginx config:

location /v2ray {
    proxy_redirect off;
    proxy_pass http://127.0.0.1:10000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Now when someone visits https://mosearc.eu/v2ray, Nginx upgrades the connection to a WebSocket and forwards it to Xray. To anyone inspecting the traffic, it looks like a normal HTTPS connection to my portfolio site that happens to use WebSockets, extremely common and completely benign-looking.

Client Setup

Installed Xray on my laptop too, configured it as a client that creates a local SOCKS5 proxy:

{
  "inbounds": [
    {"port": 10808, "listen": "127.0.0.1", "protocol": "socks", "settings": {"udp": true}},
    {"port": 10809, "listen": "127.0.0.1", "protocol": "http"}
  ],
  "outbounds": [{
    "protocol": "vless",
    "settings": {
      "vnext": [{
        "address": "mosearc.eu",
        "port": 443,
        "users": [{"id": "[UUID]", "encryption": "none"}]
      }]
    },
    "streamSettings": {
      "network": "ws",
      "security": "tls",
      "tlsSettings": {"serverName": "mosearc.eu"},
      "wsSettings": {"path": "/v2ray"}
    }
  }]
}

Notice we use mosearc.eu here (the Cloudflare-proxied domain), not the VPN subdomain. Because this IS real HTTPS Cloudflare can proxy it normally. The traffic goes through Cloudflare's edge network, gets forwarded to my home server, hits Nginx, gets upgraded to WebSocket, and reaches Xray. To the university DPI, it's indistinguishable from someone browsing my portfolio.

Quick test:

curl -x socks5://127.0.0.1:10808 https://ifconfig.me

Returned my home server's IP. The tunnel works.

Making It a Full VPN

A SOCKS proxy works for browsers (with --proxy-server flags), but games don't understand SOCKS proxies. I needed ALL system traffic to go through the tunnel, like a real VPN.

Enter tun2socks. It creates a virtual network interface (like OpenVPN's tun0) and routes traffic through a SOCKS proxy transparently. Every application thinks it's using a normal network connection.

wget https://github.com/xjasonlyu/tun2socks/releases/latest/download/tun2socks-linux-amd64.zip
unzip tun2socks-linux-amd64.zip
sudo mv tun2socks-linux-amd64 /usr/local/bin/tun2socks

The On/Off Scripts

Created two scripts for plug-and-play usage. The "on" script:

  1. Starts Xray (creates the SOCKS proxy)
  2. Saves current DNS config for later restore
  3. Routes mosearc.eu IPs directly (so Xray can reach the server)
  4. Routes DNS (1.1.1.1) directly (so domain resolution works)
  5. Creates a tun0 interface via tun2socks
  6. Sets default route through tun0

The "off" script reverses everything: removes routes, kills tun2socks, stops Xray, restores DNS.

~/DPI_bypass_on.sh     # Everything through home server
# ... game, browse, whatever ...
~/DPI_bypass_off.sh    # Back to normal

No proxychains, no browser flags, no per-app configuration. Turn it on, everything works. Turn it off, back to normal.

The Traffic Flow

Here's what the university firewall sees when I'm gaming:

My laptop โ†’ HTTPS WebSocket connection โ†’ mosearc.eu (Cloudflare CDN)
                    โ†“
University DPI: "Student browsing a portfolio website. Normal. Allow."
                    โ†“
Actually: Game traffic โ†’ tun0 โ†’ tun2socks โ†’ SOCKS proxy โ†’ Xray client
  โ†’ WebSocket over HTTPS โ†’ Cloudflare โ†’ my home server โ†’ Xray server โ†’ Internet

The DPI system literally cannot tell the difference between me browsing my own website and me playing games. Because at the protocol level, they look identical, it's all HTTPS WebSocket frames to the same domain.

Why V2Ray Beats Everything Else

Method Blocked by DPI? Why
OpenVPN (UDP) โœ… Blocked Non-standard port, distinctive handshake
OpenVPN (TCP 443) โœ… Blocked Right port, but packet patterns differ from real HTTPS
stunnel + OpenVPN โ“ Maybe works Real TLS, but complex setup, I couldn't get it working
WireGuard โœ… Blocked UDP only, very distinctive handshake
Xray (VLESS + WS + TLS) โŒ Not blocked Indistinguishable from normal HTTPS WebSocket

Reflections

This was by far the most complex thing I've built on this server. Three failed attempts (OpenVPN TCP, stunnel, broken DNS routing) before landing on something that actually works. But the result is genuinely impressive, I can game on the most restrictive Wi-Fi networks in the world, and all the IT department sees is someone visiting their own portfolio website.

Is this overkill? Absolutely. V2Ray was designed to bypass the Great Firewall of China. I'm using it to play video games at university. But that's the beauty of self-hosting, every problem is an excuse to learn something new. And now I have technology that bypasses nation-state level censorship running on a laptop in my basement. That's kind of cool.


What's Next

  • V2Ray mobile client setup
  • UPS (still waiting on the crash experiment)
  • Containerization with Docker
  • Self-hosted drive with NAS
  • VPS reverse proxy (hide home IP completely)
  • Mail server
  • Local LLM