| name | vercel-xhttp-relay |
| description | Deploy an XHTTP relay on Vercel Edge Functions to proxy Xray/V2Ray traffic and hide your origin server IP behind *.vercel.app |
| triggers | ["set up xhttp relay on vercel","proxy xray traffic through vercel","hide vps ip with vercel edge","deploy v2ray relay vercel","xhttp vercel edge function","configure xray xhttp inbound","vercel xray forwarding setup","forward xhttp traffic vercel"] |
Vercel XHTTP Relay
Skill by ara.so — Daily 2026 Skills collection.
A minimal Vercel Edge Function relay that forwards XHTTP traffic from Xray/V2Ray clients to a backend Xray server. Vercel's *.vercel.app domain acts as a CDN front, hiding your origin VPS IP from censors.
Architecture
Client (v2rayN/Hiddify)
→ TLS (SNI=vercel.com) to *.vercel.app
→ Vercel Edge Function (relay, no buffer)
→ HTTP/2 to backend Xray XHTTP inbound
Supported transports: XHTTP only
Not supported on Vercel Edge: WebSocket, gRPC, TCP, mKCP, QUIC, Reality
Prerequisites
- Linux VPS outside Iran (Ubuntu 22.04/24.04 recommended), min 1 vCPU / 1 GB RAM
- A domain with an A record pointing to your VPS (DNS only, not proxied)
- Vercel account (free Hobby tier works for light personal use)
- Node.js + npm installed locally (for Vercel CLI)
- Xray v1.8.16+ installed on VPS
Step 1 — Install Xray on VPS
apt update && apt upgrade -y
apt install -y curl socat cron ufw
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
xray version
xray uuid
Step 2 — Obtain a TLS Certificate
curl https://get.acme.sh | sh
source ~/.bashrc
~/.acme.sh/acme.sh --issue --standalone -d xray.yourdomain.com
~/.acme.sh/acme.sh --install-cert -d xray.yourdomain.com \
--key-file /etc/xray/private.key \
--fullchain-file /etc/xray/cert.crt \
--reloadcmd "systemctl restart xray"
Step 3 — Xray Server Config (XHTTP Inbound)
/etc/xray/config.json:
{
"log": { "loglevel": "warning" },
"inbounds": [
{
"port": 443,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "$YOUR_UUID",
"flow": ""
}
],
"decryption": "none"
},
"streamSettings": {
"network": "xhttp",
"security": "tls",
"tlsSettings": {
systemctl restart xray
systemctl enable xray
systemctl status xray
Step 4 — The Vercel Edge Relay (api/proxy.js)
The relay function streams request bodies and responses without buffering:
export const config = { runtime: 'edge' };
const BACKEND = process.env.XRAY_BACKEND_URL;
export default async function handler(req) {
if (!BACKEND) {
return new Response('Backend not configured', { status: 500 });
}
const url = new URL(req.url);
const targetUrl = BACKEND + url.pathname.replace(/^\/proxy/, '') + url.search;
const headers = new Headers();
for (const [key, value] of req.headers.entries()) {
if (key.toLowerCase() !== 'host') {
headers.set(key, value);
}
}
try {
const response = (targetUrl, {
: req.,
headers,
: req. !== && req. !== ? req. : ,
: ,
});
(response., {
: response.,
: response.,
: response.,
});
} (err) {
(, { : });
}
}
Step 5 — vercel.json
{
"rewrites": [
{ "source": "/(.*)", "destination": "/api/proxy" }
]
}
Step 6 — package.json (minimal)
{
"name": "vercel-xhttp-relay",
"version": "1.0.0",
"private": true
}
Step 7 — Deploy to Vercel
Via CLI
npm i -g vercel
vercel login
vercel
vercel env add XRAY_BACKEND_URL
vercel --prod
Via Dashboard (no CLI needed)
- Push repo to GitHub
- Go to vercel.com/new → Import repo
- Settings → Environment Variables → add
XRAY_BACKEND_URL
- Deploy
Your relay URL will be: https://your-project.vercel.app
Step 8 — Client Configuration (v2rayN / Hiddify)
| Field | Value |
|---|
| Protocol | VLESS |
| Address | your-project.vercel.app |
| Port | 443 |
| UUID | $YOUR_UUID (from xray uuid) |
| Transport | XHTTP |
| Path | /your-secret-path |
| TLS | TLS |
| SNI | vercel.com |
| Fingerprint | chrome |
| Flow | (empty) |
VLESS link format:
vless://$YOUR_UUID@your-project.vercel.app:443?encryption=none&security=tls&sni=vercel.com&fp=chrome&type=xhttp&path=%2Fyour-secret-path#MyVercelRelay
Project File Structure
vercel-xhttp-relay/
├── api/
│ └── proxy.js # Edge Function relay
├── vercel.json # Rewrite rules
└── package.json
Environment Variables
| Variable | Description | Example |
|---|
XRAY_BACKEND_URL | Full URL to your Xray XHTTP inbound | https://xray.yourdomain.com:443/secret-path |
Set via CLI:
vercel env add XRAY_BACKEND_URL production
vercel env add XRAY_BACKEND_URL preview
Or in vercel.json for non-secret config (not recommended for backend URLs):
{
"env": {
"XRAY_BACKEND_URL": "@xray-backend-url"
}
}
Firewall Setup on VPS
ufw allow 22/tcp
ufw allow 443/tcp
ufw enable
ufw status
Common Patterns
Multiple Vercel Projects for Failover
Deploy the same relay to multiple Vercel accounts and configure load balance in v2rayN:
{
"tag": "balancer",
"selector": ["relay1", "relay2", "relay3"]
}
Check Vercel Usage
vercel billing
Redeploy After Config Change
vercel --prod
View Logs
vercel logs your-project.vercel.app --follow
Troubleshooting
502 Bad Gateway from Vercel
systemctl status xray
journalctl -u xray -f
ufw status
ss -tlnp | grep 443
curl -v https://xray.yourdomain.com:443/your-secret-path
DNS Not Resolving
dig @8.8.8.8 xray.yourdomain.com +short
Resolve-DnsName xray.yourdomain.com -Server 8.8.8.8 -Type A
DNS record must be DNS only (grey cloud in Cloudflare), not proxied.
TLS Certificate Issues
~/.acme.sh/acme.sh --list
~/.acme.sh/acme.sh --renew -d xray.yourdomain.com --force
openssl x509 -in /etc/xray/cert.crt -noout -dates
Xray Config Validation
xray run -test -c /etc/xray/config.json
Client Can't Connect
- Confirm SNI is set to
vercel.com, not your project domain
- Confirm transport is
xhttp (not ws, grpc, etc.)
- Confirm path matches exactly between server config and client
- Check UUID matches exactly
Vercel Account Paused (Hobby Bandwidth Exceeded)
Hobby plan counts bandwidth twice (client↔Vercel + Vercel↔origin). To avoid:
- Exclude 4K video and large downloads from the proxy
- Use multiple Hobby accounts with failover
- Upgrade to Pro ($20/month) and set Spend Management limits
Vercel Hobby Plan Limits (Reference)
| Resource | Limit |
|---|
| Edge Function invocations | 500,000 / month |
| Fast Origin Transfer | 10 GB / month (counts both directions) |
| Bandwidth (to client) | 100 GB / month |
| Edge Function duration | 30 seconds max per request |
Monitor at Vercel Dashboard → Usage. Vercel sends email alerts at 80% and 100%.
Key Commands Cheatsheet
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install
xray uuid
xray version
xray run -test -c /etc/xray/config.json
systemctl restart xray && systemctl status xray
journalctl -u xray -f
npm i -g vercel
vercel login
vercel
vercel --prod
vercel env add XRAY_BACKEND_URL
vercel logs your-project.vercel.app --follow
vercel billing