Files
3x-ui-pro/CLAUDE.md
T

5.3 KiB

x-ui-pro-refactor

Refactored single-file installer for 3x-ui VPN panel (based on x-ui-pro).

Repository structure

x-ui-latest.sh          — main installer script (single file, run remotely)
x-ui-patch.sh           — apply current features to an existing install (no DB changes)
x-ui-adguard.sh         — optional: AdGuard Home on the panel domain (DoH at
                          /dns-query, admin UI at random /adg-<rand>/ path)
assets/
  backup/x-ui-backup.sh — backup / restore / list script
  clash/clash.yaml      — Clash/Mihomo subscription template (served by UA sniffing)
  diagnostics/
    index.html          — network diagnostics page (speed test, MTR, test files)
    mtr-backend.py      — localhost-only backend: MTR, LibreSpeed endpoints, clash.yaml generator
    librespeed/         — vendored LibreSpeed engine (speedtest.js, speedtest_worker.js, LGPL)
  fake-sites/
    site-01 … site-50/  — static HTML cover pages (index.html per site)

Scripts download assets at install time from this repo's raw GitHub URL (https://git.pub38.com/Evgen/3x-ui-pro/main/...) — changes take effect on servers only after push to main.

What x-ui-latest.sh does

  1. Checks OS (Ubuntu 24.04/26.04, Debian 12/13) and rejects QEMU-emulated CPUs
  2. Parses CLI arguments (-install n to skip package install — default y, -subdomain, -reality_domain, -auto_domain y, -version <ver>, -uninstall y)
  3. Validates domains (panel ≠ REALITY), then stops/cleans any previous install
  4. Installs packages (install_packages) — nginx-full, certbot, sqlite3, ufw, mtr, python3 …
  5. Obtains Let's Encrypt certs via certbot standalone (get_ssl_certs) — panel + reality domains
  6. Installs 3x-ui panel from MHSanaei/3x-ui latest release (install_panel)
  7. Configures nginx (configure_nginx) — SNI stream (443 → reality:8443 / panel:7443), per-domain vhosts, shared includes snippet, rate-limit zones
  8. Pushes all settings and inbounds into x-ui.db (configure_xui_db). Share-link endpoints use the hosts table (supersedes legacy externalProxy arrays in stream_settings): one host per inbound — REALITY gets security=same, the rest front through nginx :443 with security=tls, fingerprint firefox
  9. Installs Clash subscription template (install_clash_sub) → /var/www/subpage/clash.yaml.tpl; Clash/Mihomo user agents get generated clash.yaml, ?provider=1 bypasses it
  10. Downloads a random fake cover site (install_fake_site) → /var/www/html/
  11. Installs network diagnostics (install_diagnostics) → /var/www/diagnostics/ + mtr-backend systemd service (hardened, dedicated user, localhost-only). Access is gated by the 3x-ui panel session via an nginx SSO bridge at /<panel_path>/diag: auth_request validates the session against GET <basePath>/panel/ (with X-Requested-With → 401 instead of a login redirect), then issues a path-scoped diag_key cookie and redirects to the diag page. Hitting the diag path directly without the cookie 302-bounces through that bridge, so a bookmarked diag link "just works" once logged into the panel (API/asset sub-locations stay 404 without the cookie). The 3x-ui session cookie is Path-scoped to the panel base path, which is why the bridge must live under the panel path
  12. Tunes kernel/BBR (tune_system)
  13. Sets up cron (setup_cron) — daily x-ui restart + nginx reload; monthly certbot renew with pre/post hooks stopping/starting nginx (certs are standalone-issued)
  14. Configures UFW (setup_firewall) — 22/80/443 tcp, 443 udp
  15. Prints panel URL + credentials (show_results)

Speed test (LibreSpeed)

Upload over HTTP/2 is throttled per-stream by the h2 flow-control window, so a single big POST measures ~4x low. The diagnostics page uses the vendored LibreSpeed engine: parallel streams, XHR-progress measurement, no telemetry, no database. h2 must stay enabled on the vhosts — trojan-gRPC needs it. Endpoints: download = static testfiles/test-100m.bin; upload = api/st/up (Python sink, proxy_request_buffering off); ping = api/st/ping (nginx return 200); IP = api/st/getip. Speedtest locations use limit_conn, not limit_req (the engine fires many requests).

Inbounds created

Protocol Port Transport
vless 8443 REALITY / TCP
vless $ws_port WebSocket
vless UDS socket XHTTP (gRPC)
trojan $trojan_port gRPC

Running

bash <(curl -fsSL https://git.pub38.com/Evgen/3x-ui-pro/main/x-ui-latest.sh) \
  -subdomain panel.example.com -reality_domain r.example.com

Patch an existing install (re-reads ports/paths from x-ui.db and nginx):

bash <(curl -fsSL https://git.pub38.com/Evgen/3x-ui-pro/main/x-ui-patch.sh)

Add AdGuard Home on the panel domain (standalone, re-run safe, -uninstall y to remove). AGH binds localhost only; nginx bridges /dns-query (DoH, allow_unencrypted_doh) and a random /adg-<rand>/ admin path via snippets/adguard.conf included in the panel vhost. Installer/patch regenerate the vhost and drop that include — re-run this script after them:

bash <(curl -fsSL https://git.pub38.com/Evgen/3x-ui-pro/main/x-ui-adguard.sh)