├── Dockerfile ├── README.md ├── app.json ├── config.json ├── config_client ├── trojan_tcp_tls.json ├── vless_tcp_tls.json ├── vless_tcp_xtls.json ├── vless_ws_tls.json ├── vmess_tcp_tls.json └── vmess_ws_tls.json ├── entrypoint.sh └── heroku.yml /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM teddysun/xray 2 | RUN apk add --no-cache acme.sh socat gettext nginx \ 3 | && mkdir /run/nginx \ 4 | && sed -i 's/user nginx/#&/' /etc/nginx/nginx.conf \ 5 | && sed -ie 's/80/18080/g; 10 d; 9 a root /var/www;\nautoindex on;' /etc/nginx/conf.d/default.conf \ 6 | && echo "Hello World" > /var/www/index.html \ 7 | && curl -sSL https://github.com/cloudflare/wrangler/releases/download/v1.18.0/wrangler-v1.18.0-x86_64-unknown-linux-musl.tar.gz | tar zxf - \ 8 | && mv dist/wrangler /usr/bin/ \ 9 | && rm -rf dist/ 10 | ADD config_client /xray/client/ 11 | ADD config.json /xray/config.json 12 | ADD entrypoint.sh / 13 | CMD /entrypoint.sh 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # heroku-xray 2 | 3 | [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) 4 | 5 | 在 Heroku 上部署 [Xray VLESS-TCP-XTLS-WHATEVER](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-WHATEVER),实现 VLESS over TCP with XTLS + 回落 & 分流 to WHATEVER(终极配置)。 6 | 7 | --- 8 | 9 | 这里是 [进阶配置](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-TLS-WS%20(recommended)) 的超集,利用 VLESS 强大的回落分流特性,实现了 443 端口尽可能多的协议、配置的完美共存,包括 [XTLS Direct Mode](https://github.com/rprx/v2fly-github-io/blob/master/docs/config/protocols/vless.md#xtls-%E9%BB%91%E7%A7%91%E6%8A%80) 10 | 11 | 客户端可以同时通过下列方式连接到服务器,其中 WS 都可以通过 CDN 12 | 13 | 1. [VLESS over TCP with XTLS](https://myxray.myname.workers.dev/vless_tcp_xtls.json),数倍性能,首选方式 14 | 2. VLESS over TCP with TLS 15 | 3. VLESS over WS with TLS 16 | 4. VMess over TCP with TLS,不推荐 17 | 5. VMess over WS with TLS 18 | 6. Trojan over TCP with TLS 19 | 20 | --- 21 | 22 | 这里设置默认回落到 Xray 的 Trojan 协议,再继续回落到 80 端口的 Web 服务器(也可以换成数据库、FTP 等) 23 | 24 | ## 部署步骤 25 | 26 | * 点击上方 `Deploy to Heroku` 按钮,重定向到Heroku新增APP界面。 27 | * 输入 `App name`,比如`myxray`。 28 | * 访问 [uuid.online](http://www.uuid.online/) 获取一个随机 UUID 填写到 `UUID` 参数中,比如`9f6ef794-04fd-4961-bc6f-d2bcfebe4649`。 29 | * 输入这个 Heroku App 的访问域名,比如`myxray.herokuapp.com`。如果要使用 Cloudflare CDN 加速,请先部署好`Worker`并输入`Worker`的域名,比如`myxray.myname.workers.dev`。 30 | * 点击 `Deploy app` 按钮,等待部署完成。 31 | 32 | ## 客户端配置 33 | 34 | 参考 [Xray VLESS-TCP-XTLS-WHATEVER client config](https://github.com/XTLS/Xray-examples/tree/main/VLESS-TCP-XTLS-WHATEVER/config_client) 配置。 35 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heroku-xray", 3 | "description": "Deploy xray VLESS-TCP-XTLS-WHATEVER on Heroku.", 4 | "keywords": ["heroku", "xray"], 5 | "env": { 6 | "UUID": { 7 | "description": "The ID for vmess/vless and password for trojan.", 8 | "value": "fe919394-f173-4391-aabf-b98c8a499218" 9 | }, 10 | "DOMAIN": { 11 | "description": "The Cloudflare worker domain or Heroku app domain used by end user.", 12 | "value": "app-name.seanz.workers.dev" 13 | }, 14 | "CF_KV_API_TOKEN": { 15 | "description": "The Cloudflare API token with Workers KV permission.", 16 | "value": "YXTcXnhf2uIIa9YoRcc8x0TmI8GdAy4d9jtYJxC4" 17 | }, 18 | "CF_KV_NAMESPACE_ID": { 19 | "description": "The Cloudflare KV namespace ID.", 20 | "value": "4d7b910f88a846329264bf31cc3f5db4" 21 | } 22 | }, 23 | "website": "http://heroku-xray.herokuapp.com", 24 | "repository": "https://github.com/zhangsean/heroku-xray", 25 | "stack": "container" 26 | } 27 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "info" 4 | }, 5 | "inbounds": [ 6 | { 7 | "port": $PORT, 8 | "protocol": "vless", 9 | "settings": { 10 | "clients": [ 11 | { 12 | "id": "$UUID", 13 | "flow": "xtls-rprx-direct", 14 | "level": 0 15 | } 16 | ], 17 | "decryption": "none", 18 | "fallbacks": [ 19 | { 20 | "dest": 1310, 21 | "xver": 1 22 | }, 23 | { 24 | "path": "/vless", 25 | "dest": 1234, 26 | "xver": 1 27 | }, 28 | { 29 | "path": "/vmesstcp", 30 | "dest": 2345, 31 | "xver": 1 32 | }, 33 | { 34 | "path": "/vmessws", 35 | "dest": 3456, 36 | "xver": 1 37 | } 38 | ] 39 | }, 40 | "streamSettings": { 41 | "network": "tcp", 42 | "security": "xtls", 43 | "xtlsSettings": { 44 | "alpn": [ 45 | "http/1.1", 46 | "h2" 47 | ], 48 | "certificates": [ 49 | { 50 | "certificateFile": "/root/ssl.cer", 51 | "keyFile": "/root/ssl.key" 52 | } 53 | ] 54 | } 55 | } 56 | }, 57 | { 58 | "port": 1310, 59 | "listen": "127.0.0.1", 60 | "protocol": "trojan", 61 | "settings": { 62 | "clients": [ 63 | { 64 | "password": "$UUID", 65 | "level": 0 66 | } 67 | ], 68 | "fallbacks": [ 69 | { 70 | "dest": 18080 71 | } 72 | ] 73 | }, 74 | "streamSettings": { 75 | "network": "tcp", 76 | "security": "none", 77 | "tcpSettings": { 78 | "acceptProxyProtocol": true 79 | } 80 | } 81 | }, 82 | { 83 | "port": 1234, 84 | "listen": "127.0.0.1", 85 | "protocol": "vless", 86 | "settings": { 87 | "clients": [ 88 | { 89 | "id": "$UUID", 90 | "level": 0 91 | } 92 | ], 93 | "decryption": "none" 94 | }, 95 | "streamSettings": { 96 | "network": "ws", 97 | "security": "none", 98 | "wsSettings": { 99 | "acceptProxyProtocol": true, 100 | "path": "/vless" 101 | } 102 | } 103 | }, 104 | { 105 | "port": 2345, 106 | "listen": "127.0.0.1", 107 | "protocol": "vmess", 108 | "settings": { 109 | "clients": [ 110 | { 111 | "id": "$UUID", 112 | "level": 0 113 | } 114 | ] 115 | }, 116 | "streamSettings": { 117 | "network": "tcp", 118 | "security": "none", 119 | "tcpSettings": { 120 | "acceptProxyProtocol": true, 121 | "header": { 122 | "type": "http", 123 | "request": { 124 | "path": [ 125 | "/vmesstcp" 126 | ] 127 | } 128 | } 129 | } 130 | } 131 | }, 132 | { 133 | "port": 3456, 134 | "listen": "127.0.0.1", 135 | "protocol": "vmess", 136 | "settings": { 137 | "clients": [ 138 | { 139 | "id": "$UUID", 140 | "level": 0 141 | } 142 | ] 143 | }, 144 | "streamSettings": { 145 | "network": "ws", 146 | "security": "none", 147 | "wsSettings": { 148 | "acceptProxyProtocol": true, 149 | "path": "/vmessws" 150 | } 151 | } 152 | } 153 | ], 154 | "outbounds": [ 155 | { 156 | "protocol": "freedom" 157 | } 158 | ] 159 | } 160 | -------------------------------------------------------------------------------- /config_client/trojan_tcp_tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "warning" 4 | }, 5 | "inbounds": [ 6 | { 7 | "port": 10800, 8 | "listen": "127.0.0.1", 9 | "protocol": "socks", 10 | "settings": { 11 | "udp": true 12 | } 13 | } 14 | ], 15 | "outbounds": [ 16 | { 17 | "protocol": "trojan", 18 | "settings": { 19 | "servers": [ 20 | { 21 | "address": "$DOMAIN", 22 | "port": 443, 23 | "password": "$UUID", 24 | "level": 0 25 | } 26 | ] 27 | }, 28 | "streamSettings": { 29 | "network": "tcp", 30 | "security": "tls", 31 | "tlsSettings": { 32 | "serverName": "$DOMAIN" 33 | } 34 | } 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /config_client/vless_tcp_tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "warning" 4 | }, 5 | "inbounds": [ 6 | { 7 | "port": 10800, 8 | "listen": "127.0.0.1", 9 | "protocol": "socks", 10 | "settings": { 11 | "udp": true 12 | } 13 | } 14 | ], 15 | "outbounds": [ 16 | { 17 | "protocol": "vless", 18 | "settings": { 19 | "vnext": [ 20 | { 21 | "address": "$DOMAIN", 22 | "port": 443, 23 | "users": [ 24 | { 25 | "id": "$UUID", 26 | "encryption": "none", 27 | "level": 0 28 | } 29 | ] 30 | } 31 | ] 32 | }, 33 | "streamSettings": { 34 | "network": "tcp", 35 | "security": "tls", 36 | "tlsSettings": { 37 | "serverName": "$DOMAIN" 38 | } 39 | } 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /config_client/vless_tcp_xtls.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "warning" 4 | }, 5 | "inbounds": [ 6 | { 7 | "port": 10800, 8 | "listen": "127.0.0.1", 9 | "protocol": "socks", 10 | "settings": { 11 | "udp": true 12 | } 13 | } 14 | ], 15 | "outbounds": [ 16 | { 17 | "protocol": "vless", 18 | "settings": { 19 | "vnext": [ 20 | { 21 | "address": "$DOMAIN", 22 | "port": 443, 23 | "users": [ 24 | { 25 | "id": "$UUID", 26 | "flow": "xtls-rprx-direct", 27 | "encryption": "none", 28 | "level": 0 29 | } 30 | ] 31 | } 32 | ] 33 | }, 34 | "streamSettings": { 35 | "network": "tcp", 36 | "security": "xtls", 37 | "xtlsSettings": { 38 | "serverName": "$DOMAIN" 39 | } 40 | } 41 | } 42 | ] 43 | } -------------------------------------------------------------------------------- /config_client/vless_ws_tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "warning" 4 | }, 5 | "inbounds": [ 6 | { 7 | "port": 10800, 8 | "listen": "127.0.0.1", 9 | "protocol": "socks", 10 | "settings": { 11 | "udp": true 12 | } 13 | } 14 | ], 15 | "outbounds": [ 16 | { 17 | "protocol": "vless", 18 | "settings": { 19 | "vnext": [ 20 | { 21 | "address": "$DOMAIN", 22 | "port": 443, 23 | "users": [ 24 | { 25 | "id": "$UUID", 26 | "encryption": "none", 27 | "level": 0 28 | } 29 | ] 30 | } 31 | ] 32 | }, 33 | "streamSettings": { 34 | "network": "ws", 35 | "security": "tls", 36 | "tlsSettings": { 37 | "serverName": "$DOMAIN" 38 | }, 39 | "wsSettings": { 40 | "path": "/vless" 41 | } 42 | } 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /config_client/vmess_tcp_tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "warning" 4 | }, 5 | "inbounds": [ 6 | { 7 | "port": 10800, 8 | "listen": "127.0.0.1", 9 | "protocol": "socks", 10 | "settings": { 11 | "udp": true 12 | } 13 | } 14 | ], 15 | "outbounds": [ 16 | { 17 | "protocol": "vmess", 18 | "settings": { 19 | "vnext": [ 20 | { 21 | "address": "$DOMAIN", 22 | "port": 443, 23 | "users": [ 24 | { 25 | "id": "$UUID", 26 | "security": "none", 27 | "level": 0 28 | } 29 | ] 30 | } 31 | ] 32 | }, 33 | "streamSettings": { 34 | "network": "tcp", 35 | "security": "tls", 36 | "tlsSettings": { 37 | "serverName": "$DOMAIN" 38 | }, 39 | "tcpSettings": { 40 | "header": { 41 | "type": "http", 42 | "request": { 43 | "path": [ 44 | "/vmesstcp" 45 | ] 46 | } 47 | } 48 | } 49 | } 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /config_client/vmess_ws_tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "loglevel": "warning" 4 | }, 5 | "inbounds": [ 6 | { 7 | "port": 10800, 8 | "listen": "127.0.0.1", 9 | "protocol": "socks", 10 | "settings": { 11 | "udp": true 12 | } 13 | } 14 | ], 15 | "outbounds": [ 16 | { 17 | "protocol": "vmess", 18 | "settings": { 19 | "vnext": [ 20 | { 21 | "address": "$DOMAIN", 22 | "port": 443, 23 | "users": [ 24 | { 25 | "id": "$UUID", 26 | "security": "none", 27 | "level": 0 28 | } 29 | ] 30 | } 31 | ] 32 | }, 33 | "streamSettings": { 34 | "network": "ws", 35 | "security": "tls", 36 | "tlsSettings": { 37 | "serverName": "$DOMAIN" 38 | }, 39 | "wsSettings": { 40 | "path": "/vmessws" 41 | } 42 | } 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | wrangler init 3 | echo $CF_KV_API_TOKEN | wrangler config 4 | ACCOUNT_ID=`wrangler whoami | sed -n '/s Account/s/.*Account | \(.*\) |/\1/p'` 5 | sed -i "/account_id/s/''/'$ACCOUNT_ID'/" wrangler.toml 6 | wrangler kv:key get cer -n $CF_KV_NAMESPACE_ID > ssl.cer 7 | if grep 'key not found' ssl.cer; then 8 | rm -f ssl.cer 9 | acme.sh --issue --standalone -d $DOMAIN --httpport $PORT --tlsport $PORT 10 | ln -s ~/.acme.sh/$DOMAIN/fullchain.cer ssl.cer 11 | ln -s ~/.acme.sh/$DOMAIN/$DOMAIN.key ssl.key 12 | wrangler kv:key put -n $CF_KV_NAMESPACE_ID cer ssl.cer --path 13 | wrangler kv:key put -n $CF_KV_NAMESPACE_ID key ssl.key --path 14 | else 15 | wrangler kv:key get key -n $CF_KV_NAMESPACE_ID > ssl.key 16 | fi 17 | envsubst < /xray/config.json > /etc/xray/config.json 18 | cd /xray/client 19 | for f in $(ls -1 *.json); do envsubst < $f > /var/www/$f; done 20 | nginx 21 | xray -c /etc/xray/config.json 22 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | web: Dockerfile 4 | --------------------------------------------------------------------------------