Translated by Google Translate from ๐ฎ๐ท Complete Persian Setup Guide.
Telegram Channel
๐ข Avaco Cloud Telegram Channel โ Join for more tutorials, updates, censorship bypass methods and new configs:
A minimal relay running on Vercel Edge Functions that forwards XHTTP traffic from your Xray/V2Ray client to your backend Xray server. The goal: use Vercel's global edge network and the *.vercel.app domain as a front to hide the real IP of your origin server.
This project is only useful if you have an Xray server with XHTTP and want to mask its IP with Vercel.
โ It is not useful for you if:
โ It is useful for you if:
+-----------+ +-------------+ +---------------+ | Client | TLS, SNI=vercel.com | Vercel Edge | HTTP/2 | Xray server | | (v2rayN/ +---------------------->| (relay) +---------->| XHTTP inbound | | Hiddify) | XHTTP request | | forward | | +-----------+ +-------------+ +---------------+
The client connects to the Vercel domain with SNI=vercel.com. To the censor, it looks like normal Vercel traffic. The Vercel Edge Function forwards the request body to the Xray server without buffering. The response is streamed back in the same way.
๐ด Important Warning โ Fast Origin Transfer: In the Hobby plan, each byte of traffic is counted twice (once clientโVercel and once Vercelโserver). If the quota is reached, Vercel will pause your account, users will no longer be able to connect and you will have to wait 30 days or purchase Pro. Details in Vercel Limitations section.
โ ๏ธ XHTTP only: WebSocket, gRPC, TCP, mKCP, QUIC and Reality do not work on Vercel Edge (runtime limitation).
โ ๏ธ Vercel TOS: Using a proxy may violate the TOS. If traffic is high, your account may be suspended. Keep traffic balanced.
โ ๏ธ Educational: This repo is for personal training and testing, not production. No SLA or support.
To avoid disconnection in the middle of the month:
This guide is written for both Windows and Mac/Linux. At each step you will see the equivalent commands depending on your OS.
All of the following tools are free:
๐ก Recommendation: After installing Git for Windows, use Git Bash, as Unix commands (like curl, ssh, cat) work naturally inside it. It makes this guide easier.
Note the public IP and the SSH password from the dashboard.
If you donโt have a domain:
Set up an A Record (on Cloudflare):
๐ด Proxy status must be DNS only. If you set it to Proxied, Cloudflare will get caught in the middle of the traffic and wonโt work.
๐ Mac / ๐ง Linux:
dig @8.8.8.8 xray.yourdomain.com +short
๐ช Windows (PowerShell):
Resolve-DnsName xray.yourdomain.com -Server 8.8.8.8 -Type A
Or with nslookup:
nslookup xray.yourdomain.com 8.8.8.8
๐ช Windows (Git Bash):
nslookup xray.yourdomain.com 8.8.8.8
It should return your server IP. This may take 1-5 minutes.
๐ Mac / ๐ง Linux / ๐ช Windows (PowerShell or Git Bash)
ssh root@YOUR_VPS_IP
First type yes. Enter the password (it won't show as you type โ this is normal).
๐ช Windows with PuTTY (if CLI is difficult)
๐ก After this step, all commands starting with # (or root@vps:~#) will be executed inside the SSH server โ it doesn't matter if you're connecting from Mac or Windows.
Check OS:
cat /etc/os-release
You should see Ubuntu 22.04 or 24.04.
Update the system and install packages:
apt update && apt upgrade -y
apt install -y curl socat cron ufw
Install Xray with the official script:
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
After a few seconds:
info: Xray vXX.X.X is installed
Check version:
xray version
โ ๏ธ The version must be at least v1.8.16 (for XHTTP). Newer versions (like 26.x) are better.
Generate UUID:
xray uuid
Output like:
xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Save this UUID โ you will need it later in the server and client config.
Enable the servicesystemctl enable xray
Firewall configuration:
ufw allow 22/tcp # SSH
ufw allow 80/tcp # for issuing cert
ufw allow 443/tcp # for future
ufw allow 2096/tcp # our Xray port
ufw --force enable
ufw status
โ ๏ธ Before ufw --force enable, make sure port 22 is allowed, otherwise SSH connection will be dropped.
We will use acme.sh + Let's Encrypt (free, automatic).
Install acme.sh:
curl https://get.acme.sh | sh -s email=your@email.com
source ~/.bashrc
Set the default CA:
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
Make sure port 80 is free:
ss -tlnp | grep :80
If there is anything listening (like apache or nginx), stop it:
systemctl stop apache2 2>/dev/null
systemctl disable apache2 2>/dev/null
systemctl stop nginx 2>/dev/null
Issue a certificate:
~/.acme.sh/acme.sh --issue -d xray.yourdomain.com --standalone -k ec-256
Replace xray.yourdomain.com with your actual domain.
Successful output:
Cert success.
Your cert is in: /root/.acme.sh/xray.yourdomain.com_ecc/...
Install cert in Xray path:
mkdir -p /etc/xray
~/.acme.sh/acme.sh --install-cert -d xray.yourdomain.com --ecc --fullchain-file /etc/xray/cert.pem --key-file /etc/xray/key.pem --reloadcmd "systemctl restart xray"
chown -R nobody:nogroup /etc/xray
chmod 644 /etc/xray/cert.pem
chmod 640 /etc/xray/key.pem
ls -la /etc/xray/
You should see cert.pem and key.pem.
Prepare Logs:
mkdir -p /var/log/xray
touch /var/log/xray/access.log /var/log/xray/error.log
chown -R nobody:nogroup /var/log/xray
cp /usr/local/etc/xray/config.json /usr/local/etc/xray/config.json.bak 2>/dev/null || true
Write config:
๐ Before pasting, keep these values โโin mind:
cat > /usr/local/etc/xray/config.json << 'EOF'
{
"log": {
"loglevel": "warning",
"access": "/var/log/xray/access.log",
"error": "/var/log/xray/error.log"
},
"inbounds": [
{
"tag": "xhttp-in",
"listen": "0.0.0.0",
"port": 2096,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "YOUR-UUID-HERE",
"flow": ""
}
],
"decryption": "none"
},
"streamSettings": {
"network": "xhttp",
"security": "tls",
"tlsSettings": {
"alpn": [
"h2",
"http/1.1"
],
"certificates": [
{
"certificateFile": "/etc/xray/cert.pem",
"keyFile": "/etc/xray/key.pem"
}
]
},
"xhttpSettings": {
"path": "/yourpath",
"host": "xray.yourdomain.com",
"mode": "auto"
}
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "blocked"
}
]
}
EOF
โ ๏ธ After pasting, open the file with:
nano /usr/local/etc/xray/config.json
Open it and replace the three values โโYOUR-UUID-HERE, /yourpath, xray.yourdomain.com with the actual values.
Test the config syntax:
xray -test -config /usr/local/etc/xray/config.json
You should see: Configuration OK.
Start Xray:
systemctl restart xray
systemctl status xray --no-pager
ss -tlnp | grep 2096
Test locally:
curl -vk https://127.0.0.1:2096/yourpath
If you get an HTTP/2 404 โ Great! (404 is normal because you didn't send a UUID, which means Xray is working.)
Two ways: CLI (faster) or Dashboard (with GitHub).
Install Node.js and Vercel CLI
๐ Mac:
# If you don't have Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install node
sudo npm i -g vercel
vercel --version
๐ง Linux (Ubuntu/Debian):
sudo apt update
sudo apt install -y nodejs npm
sudo npm i -g vercel
vercel --version
๐ช Windows:
Install Node.js:
Install Vercel CLI (in PowerShell or Git Bash):
npm i -g vercel
vercel --version
If it gives a permission error, open PowerShell As Administrator and try again.
On any operating system:
vercel login
Select Continue with Email with the arrow, enter your email, click the confirmation link.
๐ Mac / ๐ง Linux / ๐ช Git Bash:
git clone https://github.com/YOUR-USERNAME/vercel-xhttp-relay.git
cd vercel-xhttp-relay
vercel
๐ช Windows PowerShell:
git clone https://github.com/YOUR-USERNAME/vercel-xhttp-relay.git
cd vercel-xhttp-relay
vercel
Questions:
Set Environment Variable
vercel env add TARGET_DOMAIN
vercel --prod
You will get a URL like https://your-project.vercel.app.
If you enabled Vercel Authentication during setup, you need to disable it or relay won't work:
๐ Mac / ๐ง Linux / ๐ช Git Bash / ๐ช PowerShell (Win 10+):
curl -I https://your-project.vercel.app/yourpath
๐ช Windows PowerShell (native version):
Invoke-WebRequest -Uri "https://your-project.vercel.app/yourpath" -Method Head
๐ก On Windows 10+, curl.exe is installed and working.
Output Meaning
vless://YOUR-UUID@vercel.com:443?encryption=none&security=tls&sni=vercel.com&alpn=h2&fp=chrome&type=xhttp&path=%2Fyourpath&host=your-project.vercel.app&mode=auto#Vercel-Relay
Note: Replace / in path with %2F encode.
Go to browser:
It should show your VPS server IP, not Iran IP.
โ ๏ธ These numbers are from the official Vercel documentation at the time of writing this README. They may have changed โ check vercel.com/pricing and vercel.com/docs/limits for the latest updates.
General Numbers
๐ด Very important point: Fast Origin Transfer
For proxy/relay use this is more critical than Fast Data Transfer!
Every byte of your traffic is counted twice.
If Fast Origin Transfer reaches its limit, your Hobby account will be paused and you will have to wait 30 days or upgrade
๐ Fast Origin Transfer documentation
Hobby usage estimates (conservative)
Considering both Fast Data and Fast Origin:
๐ก For everyday use (chat, browsing, music, 720p video) Hobby is more than enough. For 4K streaming or heavy downloads, either go Pro or create several Hobby projects and load balance between them.
Vercel cannot reach the backend server. Check:
Env var not set or redeployed. Run:
vercel env ls
vercel --prod
Vercel Authentication is enabled. In Dashboard โ Settings โ Deployment Protection โ Disabled.
Change SNI from vercel.com to your-project.vercel.app
Set ALPN to h2 only
Mobile ISP may bottleneck *.vercel.app. Connect a Custom Domain to Vercel (Settings โ Domains).
See error log:
tail -50 /var/log/xray/error.log
Usually cert/key file access problem. Fix with:
chown -R nobody:nogroup /etc/xray /var/log/xray
Yes, but with different code (Cloudflare Workers). For WebSocket, Cloudflare Workers is better. For XHTTP, Vercel is more stable because of streaming WebStreams.
Connect a personal domain to Vercel (Settings โ Domains โ Add). Then leave the host and address the same in the client.
There is no hard limit, but create a separate UUID for each user:
"clients": [
{ "id": "uuid-1", "email": "user1@example.com" },
{ "id": "uuid-2", "email": "user2@example.com" }
]
Yes. Put the desired port in config.json, allow in ufw, and set the same port in TARGET_DOMAIN in Vercel.
vercel logs --follow
Or in Dashboard โ Project โ Logs.
xray -test -config /usr/local/etc/xray/config.json
systemctl restart xray
Yes. acme.sh creates a cron job and automatically renews every 60 days. You can test manually:
~/.acme.sh/acme.sh --renew -d xray.yourdomain.com --force --ecc
MIT โ same as the original project.
This project is for personal training and testing. Use at your own risk. Follow the laws of your country and Vercel's TOS.
If this guide was useful to you, or you have questions/suggestions, get in touch via the Telegram channel:
More tutorials โข Update configs โข Ways to bypass censorship โข Support
โญ If you found the project useful, leave a Star so it can be seen more.