صفحه اشتراک روی همان سرورِ پنل
پنل — xray-panel/backend · نود — xray-panel/node · صفحه اشتراک — xray-panel/subscription-page
صفحه اشتراک فهرست آراسته اتصالهای کاربر را به او ارائه میدهد: لینکهای
کلاینتها، کدهای QR، تاریخ انقضا و باقیمانده ترافیک. دادهها را با API-توکن از
پنل میگیرد و ساختار درونی پنل را به مرورگر افشا نمیکند. کلاینت آن را روی
دامنه sub.example.com با یک کد کوتاه در مسیر باز میکند.
این گزینه برای حالتی است که پنل و صفحه اشتراک روی یک هاست قرار دارند. کانتینرها از طریق شبکه داخلی Docker به هم متصل میشوند و فقط nginx به بیرون نگاه میکند. اگر پنل و اشتراک باید روی ماشینهای جداگانه باشند، به صفحه صفحه اشتراک روی سرور جداگانه بروید.
چه زمانی به صفحه اشتراک نیاز است
صفحه اشتراک اختصاصی زمانی لازم است که زیردامنه جداگانه sub.example.com،
طراحی خودتان و بهروزرسانیهای مستقل از پنل را میخواهید. پنل میتواند صفحه
داخلی خود را ارائه دهد، اما آن شکل دیگری دارد و روی دامنه پنل قرار دارد. تا
زمانی که در SUB_PUBLIC_DOMAIN مقداری مانند
panel.example.com/api/sub نوشته شده باشد، کلاینتها صفحه داخلی پنل را
دریافت میکنند، نه صفحهای که شما نصب کردهاید.
چه چیزهایی لازم است
| مورد | مقدار |
|---|---|
| دامنه | sub.example.com |
| پنل | روی همان هاست، کانتینر xpanel |
| API-توکن | در پنل ساخته میشود |
| Docker و Docker Compose | نصب شده باشند |
| nginx و certbot | نصب شده باشند |
| پورت محلی | 3010 |
API-توکن در پنل ساخته میشود: Settings → API Tokens → Create token. مقدار آن فقط یکبار نشان داده میشود — آن را ذخیره کنید.
هم پنل و هم صفحه اشتراک اگر درخواست بدون هدرهای X-Forwarded-For و
X-Forwarded-Proto: https برسد، اتصال را قطع میکنند. بنابراین پیش از صفحه،
پروکسی معکوس با TLS الزامی است.
فایل محیط
یک دایرکتوری و فایل محیط بسازید:
mkdir -p /opt/xlada-subpage && cd /opt/xlada-subpage
cat > .env <<'EOF'
APP_PORT=3010
# پنل در شبکه مشترک Docker — دسترسی با نام کانتینر.
REMNAWAVE_PANEL_URL=http://xpanel:3000
REMNAWAVE_API_TOKEN=paste_token_from_panel
# پیشوند مسیر، اگر صفحه در ریشه ارائه نمیشود. بدون اسلش در دو طرف.
CUSTOM_SUB_PREFIX=
# تعداد هاپهای پروکسی معکوس که هنگام تشخیص IP کلاینت مورد اعتماد است.
TRUST_PROXY=1
# X-Api-Key برای احراز هویت خارجی: Caddy با افزونه امنیتی یا Tiny Auth.
CADDY_AUTH_API_TOKEN=
# Cloudflare Zero Trust.
CLOUDFLARE_ZERO_TRUST_CLIENT_ID=""
CLOUDFLARE_ZERO_TRUST_CLIENT_SECRET=""
# پذیرش لینکهای قدیمی Marzban.
MARZBAN_LEGACY_LINK_ENABLED=false
MARZBAN_LEGACY_SECRET_KEY=
MARZBAN_LEGACY_SUBSCRIPTION_VALID_FROM=
EOF
chmod 600 .env
نامهای REMNAWAVE_* برای سازگاری حفظ شدهاند — به این ترتیب صفحه با پنل
قدیمی Remnawave هم کار میکند. متغیرهای خالی را همانطور که هستند رها کنید.
Docker Compose
services:
xpanel-subpage:
image: ghcr.io/xray-panel/subscription-page:latest
container_name: xpanel-subpage
restart: unless-stopped
env_file: .env
ports:
- 127.0.0.1:3010:3010
networks:
- xpanel-network
networks:
xpanel-network:
name: xpanel-network
external: true
شبکه xpanel-network از قبل توسط پنل ساخته شده است، بنابراین اینجا بهصورت
external متصل میشود. کانتینر را راهاندازی کنید:
docker compose up -d
docker compose logs -f xpanel-subpage
صفحه به 127.0.0.1:3010 گوش میدهد و به بیرون نگاه نمیکند: دسترسی به آن فقط
از طریق nginx ممکن است.
گواهی
ابتدا یک بلوک موقت روی پورت 80 بسازید که بررسی certbot را ارائه میدهد:
sudo mkdir -p /var/www/certbot
sudo nano /etc/nginx/sites-available/xlada-subpage
# /etc/nginx/sites-available/xlada-subpage
# بلوک موقت برای صدور گواهی.
server {
listen 80;
listen [::]:80;
server_name sub.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
sudo ln -sf /etc/nginx/sites-available/xlada-subpage /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo apt-get update && sudo apt-get install -y certbot
sudo certbot certonly --webroot -w /var/www/certbot -d sub.example.com
پیکربندی کامل nginx
فایل /etc/nginx/sites-available/xlada-subpage را با پیکربندی کامل جایگزین کنید:
# /etc/nginx/sites-available/xlada-subpage
server {
listen 80;
listen [::]:80;
server_name sub.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name sub.example.com;
ssl_certificate /etc/letsencrypt/live/sub.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sub.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:XLADA_TLS:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
location / {
proxy_pass http://127.0.0.1:3010;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
}
}
sudo nginx -t && sudo systemctl reload nginx
sudo certbot renew --dry-run
بررسی مالکیت دامنه هنگام تمدید هم لازم است، بنابراین مسیر /var/www/certbot
را از پیکربندی حذف نکنید. پروکسیهای دیگر — در بخش
پروکسی معکوس.
تنظیم در پنل
-
توکن را در
REMNAWAVE_API_TOKENدر/opt/xlada-subpage/.envبنویسید و کانتینر را دوباره راهاندازی کنید:cd /opt/xlada-subpage && docker compose up -d -
آدرس عمومی اشتراک را در
/opt/xlada/.envپنل مشخص کنید:SUB_PUBLIC_DOMAIN=sub.example.comمقدار — بدون
https://و بدون اسلش در انتها. اگرCUSTOM_SUB_PREFIX=subتنظیم شده است،SUB_PUBLIC_DOMAIN=sub.example.com/subرا بنویسید. -
پنل را دوباره راهاندازی کنید تا فایل را بازخوانی کند:
cd /opt/xladadocker compose up -d xpanel -
لینک را بررسی کنید: Users → یک کاربر را انتخاب کنید → Subscription link. آدرس باید با
https://sub.example.com/شروع شود: پنل آن را ازSUB_PUBLIC_DOMAINو شناسه کوتاه کاربر میسازد.
TRUST_PROXY و CUSTOM_SUB_PREFIX
TRUST_PROXY=1 یعنی پیش از صفحه دقیقاً یک پروکسی معکوس وجود دارد. اگر
هاپها بیشتر باشند، مقدار تغییر میکند: جدول مقادیر — در صفحه
سرور جداگانه.
CUSTOM_SUB_PREFIX مسیر را تعیین میکند، اگر صفحه در ریشه دامنه ارائه نمیشود:
CUSTOM_SUB_PREFIX=sub
SUB_PUBLIC_DOMAIN=sub.example.com/sub
هر دو مقدار بدون اسلش نوشته میشوند و باید یکسان باشند. جزئیات بیشتر — در صفحه سرور جداگانه.
بررسی و خطاهای رایج
# کانتینر راهاندازی شده و به پورت محلی گوش میدهد
docker compose ps
docker compose logs --tail 50 xpanel-subpage
# صفحه پاسخ میدهد: انتظار HTTP/2 200 داریم
curl -sI https://sub.example.com/AbCdEf1234567890 | head -n 5
پاسخ موفق شامل HTTP/2 200 و هدر profile-web-page-url است.
| نشانه | علت و اقدام |
|---|---|
| پاسخ خالی، اتصال قطع میشود | هدرهای X-Forwarded-For یا X-Forwarded-Proto: https از دست رفتهاند؛ بلوک location در nginx را بررسی کنید |
502 Bad Gateway | کانتینر راهاندازی نشده یا nginx درخواست را به 127.0.0.1:3010 نمیفرستد |
401 یا 403 | مقدار REMNAWAVE_API_TOKEN نادرست یا باطلشده است؛ توکن جدید بسازید |
| صفحه با timeout از کار میافتد | پنل از کانتینر در دسترس نیست: اشتباه تایپی در نام xpanel یا شبکه xpanel-network متصل نشده است |
| در لاگها آدرس کلاینت اشتباه دیده میشود | مقدار TRUST_PROXY نادرست است؛ تعداد هاپهای پیش از صفحه را مشخص کنید |
404 روی لینک اشتراک | CUSTOM_SUB_PREFIX و SUB_PUBLIC_DOMAIN یکسان نیستند |
لاگهای کانتینر و nginx:
docker compose logs -f xpanel-subpage
sudo tail -f /var/log/nginx/error.log