VMess + WS + TLS with Nginx in front using X-UI panel

The sources of this post include:

Buy VPS

International lines out of China are congested. Therefore it is important to pay attention to the routing of traffic to your VPS. The most expensive option is IPLC/IEPL. Assuming you can’t or won’t go that far, the next most expensive is a VPS with CN2 GIA routing in Hong Kong. That service is offered by BandwagonHost. Again, it is still very expensive. A CN2 GIA server in Los Angeles is more affordable. To find all the CN2 GIA options on the BandwagonHost website, click CN2 GIA, read the description of the different possibilities, then scroll down and click All Services. Search the services page (Ctrl+f) for CN2 GIA.

Some other options for China are Vultr and Aliyun (Alibaba Cloud).

For Iran, the difficulty is in making payment. Try AlphaVPS, RackNerd, Noez, or Aeza.

Since IP addresses frequently get blocked, ask potential providers if you can change your server’s IP address whenever necessary.

For the VPS hardware, 1 GB of RAM is sufficient for a small number of users. Choose a recent version of Debian or Ubuntu as your operating system.

Get terminal app

You can use the terminal application in macOS or Linux to SSH into your server. Modern versions of Windows PowerShell also support SSH.

You may prefer to use a purpose-built SSH app such as PuTTY, XSHELL, or FinalShell.

Buy domain name

A cheap domain name registrar is NameSilo. You can search for top-level domains (TLDs) with a low cost for the first year. If necessary, you can just buy a new domain name when the first-year discounted domain name expires.

Add DNS record(s)

You can add your DNS record(s) at your domain name registrar, or you can switch your domain over to use Cloudflare DNS services. That gives you the option of proxying certain ports through Cloudflare. On the other hand, censors have sometimes started to block access to Cloudflare.

Once your DNS record(s) have propagated, ping your DNS name to both check DNS resolution and check access to your IP address.

Prepare server

SSH into your server using Windows PowerShell or the terminal app in Linux or macOS. Replace <SERVER-IP-ADDRESS> by your actual server IP address in the command below:

1
ssh root@<SERVER-IP-ADDRESS>

Suppress lengthy login messages:

1
touch .hushlogin

Get the existing package metadata up to date, and upgrade all existing packages:

1
apt update && apt upgrade

You may be prompted to reboot and then SSH back in again.

Protect your server with iptables, replacing <HOME-IP-ADDRESS> by your actual home IP address:

1
2
3
4
5
6
7
8
9
10
11
12
13
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s <HOME-IP-ADDRESS> -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -P INPUT DROP

ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
ip6tables -P INPUT DROP

Check that you can still access the server with these rules before you make them permanent:

1
2
3
exit

ssh root@<SERVER-IP-ADDRESS>

Make the iptables rules permanent:

1
apt install iptables-persistent

Enable BBR congestion control algorithm

Copy and paste these three commands into your SSH session to enable the Bottleneck Bandwidth and Round-trip propagation time (BBR) congestion control algorithm:

1
2
3
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
sysctl -p

Install X-UI

For English-speaking users, use the following command to install the version that supports English:

1
bash <(curl -Ls https://raw.githubusercontent.com/FranzKafkaYu/x-ui/master/install_en.sh)
  1. When asked if you want to continue, put y for yes
  2. When asked for an admin user name, put anything you want, e.g. chief, but preferably not the old default of admin
  3. When asked for a password, put a temporary password. e.g. ChangeMe!
  4. When asked for a port number, put anything you want, e.g. 9999, but preferably not the old default of 54321

First-time login

Temporarily open port 9999:

1
iptables -I INPUT -p tcp --dport 9999 -j ACCEPT

Open a browser and log in over HTTP (no security yet) on the specified port:

1
http://<FULLY-QUALIFIED-DOMAIN-NAME>:9999

Enter the admin id and password you chose a moment ago, and click login.

If necessary, switch the version of xray to the latest version.

Change the settings:

  • Set the listening IP address to 127.0.0.1 only
  • Make a note of the panel root path, which we will call <XUI-PATH>

Press Save and Restart.

Close the browser.

In your SSH session, close port 9999:

1
iptables -D INPUT -p tcp --dport 9999 -j ACCEPT

Install Nginx

Next install Nginx:

1
apt install nginx

Note that we opened ports 80 and 443 in the original iptables rules.

Install ACME

Install the recommended prerequisite:

1
apt install socat

Download and run the Automatic Certificate Management Environment (ACME) script:

1
curl https://get.acme.sh | sh

Add a soft link in /usr/local/bin, which should be in your execution path:

1
ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh

Specify the certificate authority (CA) organization:

1
acme.sh --set-default-ca --server letsencrypt

Apply for a certificate, replacing <FULLY-QUALIFIED-DOMAIN-NAME> by your server’s hostname, as specified in its DNS record:

1
acme.sh --issue -d <FULLY-QUALIFIED-DOMAIN-NAME> -k ec-256 --webroot /var/www/html

Install the certificate, replacing <FULLY-QUALIFIED-DOMAIN-NAME> by your server’s hostname:

1
acme.sh --install-cert -d <FULLY-QUALIFIED-DOMAIN-NAME> --ecc --key-file /etc/x-ui/server.key --fullchain-file /etc/x-ui/server.crt --reloadcmd "systemctl force-reload nginx"

Search for camouflage URL

A plausible camouflage website has a large amount of traffic to and from it. For this reason, the original video recommends masquerading as a file-storage server. The video shows how to search for a Cloudreve subdomain accessible over plain HTTP on port 80.

Example of search: intext:登录 Cloudreve.

Let’s call your choice <CAMOUFLAGE-DOMAIN>.

Reconfigure Nginx

Edit the configuration file /etc/nginx/nginx.conf.

1
nano /etc/nginx/nginx.conf

Delete the existing contents and completely replace as follows. Substitute in your actual values instead of the placeholders in angle brackets. Nginx checks that the camouflage URL can be resolved. If not, it will give you an error [emerg] host not found in upstream.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
worker_connections 1024;
}

http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;
gzip on;

server {
listen 443 ssl;

server_name <FULLY-QUALIFIED-DOMAIN-NAME>;
ssl_certificate /etc/x-ui/server.crt;
ssl_certificate_key /etc/x-ui/server.key;

ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;

location / {
proxy_pass http://CAMOUFLAGE-DOMAIN/;
proxy_redirect off;
proxy_ssl_server_name on;
sub_filter_once off;
sub_filter "CAMOUFLAGE-DOMAIN" $server_name;
proxy_set_header Host "CAMOUFLAGE-DOMAIN";
proxy_set_header Referer $http_referer;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header User-Agent $http_user_agent;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Accept-Encoding "";
proxy_set_header Accept-Language "zh-CN";
}

location /ray {
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;
}

location /<XUI-PATH> {
proxy_redirect off;
proxy_pass http://127.0.0.1:9999;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}

server {
listen 80;
location /.well-known/ {
root /var/www/html;
}
location / {
rewrite ^(.*)$ https://$host$1 permanent;
}
}
}

Save the file.

Check the syntax:

1
nginx -t

Every time you modify the Nginx configuration file, you must use the command:

1
systemctl reload nginx

Second-time login

Log in over HTTPS:

1
https://<FULLY-QUALIFIED-DOMAIN-NAME>/<XUI-PATH>

Now that you are logged in over HTTPS, go to settings > User Setting, and change the password ChangeMe! to a stronger password. (This permanent password has never been transmitted over insecure HTTP.)

Add Inbound

Click on the inbounds. Click Add Inbound.

  1. Put any remark
  2. Create a vmess node
  3. Listening IP 127.0.0.1
  4. The port is set to 10000
  5. Change the network to ws
  6. Make the path /ray
  7. tls is not set (because Nginx takes care of TLS)
  8. Click the plus sign by add user to add User1
  9. Click add at the bottom of the dialog box

To get the VMess URL, click Operation then QR then copy.

Download v2rayN

Download the lastest version of v2rayN from https://github.com/2dust/v2rayN/releases. The file you want is v2rayN-With-Core.zip.

Unzip the zip file.

Start the v2rayN application. If necessary, click More info and Run anyway.

Click the v2rayN icon in the system tray.

In the menu, click the three dots, and choose language en for English. Close the v2rayN panel. Exit the program on the context menu in the system tray.

Restart to see v2rayN in English.

Right-click on the v2rayN icon in the system tray. Select Set system proxy. The icon turns red.

Configure v2rayN

Paste in the VLESS URL (Ctrl+v).

  1. Change the address to be your server hostname, <FULLY-QUALIFIED-DOMAIN-NAME>
  2. Change the port to be 443
  3. Security is zero
  4. Toggle TLS to on (because you deal with Nginx before you reach x-ui)
  5. Click Confirm