Self-hosted Xray Proxy
Bypass any censorship mechanisms and content blockers
More often than not, you may find your network provider blocking, censoring, or monitoring their network traffic. For various reasons, you may want to not be blocked, censored, or monitored while accessing the internet.
In this post, I’ll show you how you can do so by setting up your own obfuscated proxy server to circumvent this issue. In particular, we’ll use the xray proxy protocol and server, which is a nearly undetectable proxy server that uses encryption similar to Transport Layer Security (TLS) to have the proxy traffic blend in with regular web traffic. This means that it will work even if the network is configured to detect and block proxies.
You will need a Linux server with ports exposed to the internet to get started. In addition, you should be somewhat familiar with the Linux command line interface; however, no god-like Linux skills are necessary. This guide is a condensed version of this somewhat poorly translated tutorial found in the xray documentation.
Setup Nginx
Our proxy server requires a TLS encryption tunnel to securely function. In order to establish a TLS tunnel, we need a TLS certificate. To obtain a TLS certificate, we will need to port forward our server on ports 80 and 443.
In addition, we need a domain name and a web server on our machine to request the certificate (either top-level domains or subdomains work). If you do not have a domain name to use, you can obtain a free subdomain at FreeDNS by signing up and add a subdomain from the sidebar. Make sure your domain points to your public IP address.
For the web server, we will install nginx. This way our certificate gets approved correctly when the certificate authority (CA) gives us a challenge. It is recommended to host a website on the server for security purposes (i.e. to disguise the fact that you are running a proxy on your server, should someone decide to check by connecting to your domain name), but it is not necessary. Our goal right now is that we just need this website to expose certain directories on our machine to satisfy the TLS certificate challenge requirements from the CA.
$ sudo apt update
$ sudo apt install nginx
Then, edit the server configuration file for nginx. You’ll need to change the server block to handle requests to our domain to prove to the CA that we actually own our domain. Change the server_name1 below to your domain and root directory to somewhere you have write permissions to (you may need to create the directory first):
$ sudoedit /etc/nginx/sites-available/default
server {
listen 80 default_server;
listen[::]:80 default_server;
# ...
root /home/admin/webroot;
server_name subdomain.domain.com;
}
$ sudo systemctl restart nginx
$ sudo systemctl enable nginx
You’ll also need to ensure that the nginx user, www-data by default, has read and write permissions to the web root. This way the web server can read and serve the files in the directories. You may also need to change the permissions of parent directories as well (for example, your home directory if you placed your web root inside it):
$ sudo chmod a+r /home/admin/webroot
TLS Certificate
Next, we can request our certificates. We can use the acme protocol to request and renew our certificate for free. This can be done by using acme.sh and Let’s Encrypt. Install acme.sh and its aliases:
$ curl https://get.acme.sh | bash
$ source .bashrc
Before actually requesting the certificate, we should first issue a test certificate. In this manner, we can avoid repeatedly requesting certificates due to local configuration errors, resulting in us hitting the rate limit for Let’s Encrypt. Afterward, if no issues occur during the test request, we can proceed to issue the actual certificate. Note that the domain and the path in the commands below are the ones that you entered in the nginx configuration file:
# Test issuing the certificate.
$ acme.sh --issue --server letsencrypt_test -d subdomain.domain.com -w /home/admin/webroot
# If everything was successful, issue the actual certificate. Otherwise, try
# browsing through acme.sh's documentation to see if you can fix the issue:
# https://github.com/acmesh-official/acme.sh/wiki/How-to-issue-a-cert
$ acme.sh --set-default-ca --server letsencrypt
$ acme.sh --issue -d subdomain.domain.com -w /home/admin/webroot --keylength ec-256 --force
Install the certificate and save the paths for use later. Because the key file isn’t readable to other users, and our proxy server runs on the user nobody by default (feel free to change it), we will also need to edit its read permissions:
$ acme.sh --installcert -d subdomain.domain.com --key-file /home/admin/cert.key --fullchain-file /home/admin/fullchain.crt --ecc
$ chmod +r /home/admin/cert.key # Add read permissions
Proxy Server
Now we can finally get to setting up the xray proxy server. As alluded to earlier, xray uses encryption similar to TLS to obfuscate the traffic, thereby making it mimic regular web traffic characteristics to any observing middle person. This is the reason why we first needed an actual TLS certificate issued by a CA, this way the handshake process will look legitimate. To install the xray server:
$ curl -fsSL https://github.com/XTLS/Xray-install/raw/main/install-release.sh | sudo bash
Generate a UUID to save for later2:
$ xray uuid
Then, edit the xray configuration file. You can check out the configuration documentation for xray. However, the documentation, like the aforementioned tutorial, is poorly written. Therefore, I have provided a general template below on what you can customize below. In order to use it, put your UUID in the “inbound > clients” section:
$ sudoedit /usr/local/etc/xray/config.json
{
// The DNS servers that your proxy server will use to query requests
// from clients. For example, if a client connects to google.com
// through your proxy, google.com will be resolved using the
// following DNS servers.
"dns": {
"servers": [
"localhost",
"1.1.1.1",
"8.8.8.8"
]
},
// Categorizing and tagging different types of client connections.
// For example, the following tags private IP, known advertizers,
// and torrenting protocol as "block". You can specify how to deal
// with each of the categories in the outbounds object below.
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "block"
},
{
"type": "field",
"domain": ["geosite:category-ads-all"],
"outboundTag": "block"
},
{
"type": "field",
"protocol": ["bittorrent"],
"outboundTag": "block"
}
]
},
// Handling client connections to the proxy. You should not change
// the port or the protocol, because they are integral to blending
// your proxy connections in HTTPS traffic. You can also add config
// for multiple clients in the client section below. This will be
// useful if you want to share your proxy server with others.
"inbounds": [
{
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "", // Put your UUID here!
"flow": "xtls-rprx-vision",
"email": "admin", // The email is mostly for
"level": 0 // identification purposes
} // in the logs. You don't
], // actually need to put
// a valid email here.
"decryption": "none",
// If the inbound traffic is not the vless protocol, we
// want to fallback to the nginx web server. This can
// help us disguise our proxy server as an actual
// website.
"fallbacks": [{
"dest": 80,
"xver": 1
}]
},
// These should be pretty self-explanatory TLS encryption
// settings. Make sure to change the certificate paths.
"streamSettings": {
"network": "tcp",
"security": "tls",
"tlsSettings": {
"alpn": "http/1.1",
"minVersion": "1.2",
"maxVersion": "1.3",
"certificates": [{
"certificateFile": "/home/admin/fullchain.crt",
"keyFile": "/home/admin/cert.key"
}]
}
}
}
],
// The outbounds section dictates how client traffic is processed
// based on the tags specified in the inbound section. Any traffic
// that isn't tagged will be sent to the first outbound. For more
// information, see the "outbounds" section on the sidebar here:
// https://xtls.github.io/en/config/outbounds/blackhole.html
"outbounds": [
{
"domainStrategy": "AsIs",
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
]
}
$ sudo systemctl enable xray
$ sudo systemctl restart xray
Proxy Client
Now that the hard part is finished, we can get to connecting to your new proxy server with clients. The clients that I would suggest you use are:
Different clients will have different user interfaces, so it is unrealistic for me to cover how to use each and every client in this blog post. However, I will list out some common settings you may encounter and what those settings will be using our example configuration file:
| Field | Value |
|---|---|
| Address/SNI | subdomain.domain.com1 |
| Flow | xtls-rprx-vision |
| ALPN | http/1.1 |
| Port | 443 |
| Protocol | vless |
| Network | TCP |
| Security | TLS |
| UUID | <Your UUID here> |
Footnotes
Have a comment or a question about this post? Reply by email!