پرش به مطلب اصلی

صفحه اشتراک روی سرور جداگانه

کد منبع

پنل — xray-panel/backend · نود — xray-panel/node · صفحه اشتراک — xray-panel/subscription-page

صفحه اشتراک فهرست آراسته اتصال‌های کاربر را به او ارائه می‌دهد: لینک‌های کلاینت‌ها، کدهای QR، تاریخ انقضا و باقی‌مانده ترافیک. داده‌ها را با API-توکن از پنل می‌گیرد. کلاینت آن را روی دامنه sub.example.com با یک کد کوتاه در مسیر باز می‌کند.

چه زمانی به سرور جداگانه نیاز است​

سرور جداگانه را زمانی برمی‌گزینند که صفحه اشتراک باید از پنل جدا باشد: بار میان ماشین‌ها تقسیم می‌شود و روی هاست پنل سرویس اضافی ظاهر نمی‌شود. صفحه از طریق دامنه https://panel.example.com به API پنل متصل می‌شود، بنابراین پنل باید از اینترنت و از طریق HTTPS در دسترس باشد. اگر پنل و اشتراک روی یک هاست قرار دارند، از صفحه صفحه اشتراک روی همان سرورِ پنل استفاده کنید.

چه چیزهایی لازم است​

موردمقدار
دامنهsub.example.com
پنلاز بیرون از طریق https://panel.example.com در دسترس باشد
API-توکندر پنل ساخته می‌شود
Docker و Docker Composeروی سرور جدید
nginx و certbotروی سرور جدید
پورت محلی3010

توکن در پنل ساخته می‌شود: Settings → API Tokens → Create token. مقدار آن فقط یک‌بار نشان داده می‌شود — آن را ذخیره کنید. این همان توکنی است که در نصب روی یک سرور با پنل استفاده می‌شود.

آماده‌سازی سرور​

sudo apt-get update
sudo apt-get install -y docker.io docker-compose-v2 nginx certbot
sudo systemctl enable --now docker
docker compose version

اگر بسته docker-compose-v2 موجود نیست، Docker Engine را بر اساس راهنمای رسمی نصب کنید و docker compose version را بررسی کنید.

فایل محیط​

mkdir -p /opt/xlada-subpage && cd /opt/xlada-subpage

cat > .env <<'EOF'
APP_PORT=3010

# پنل از بیرون و از طریق HTTPS در دسترس است.
REMNAWAVE_PANEL_URL=https://panel.example.com
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

Docker Compose​

docker-compose.yml
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
driver: bridge

روی این سرور پنل وجود ندارد، بنابراین شبکه xpanel-network به‌صورت محلی ساخته می‌شود. صفحه به 127.0.0.1:3010 گوش می‌دهد و به بیرون نگاه نمی‌کند: دسترسی به آن فقط از طریق nginx ممکن است. کانتینر را راه‌اندازی کنید:

docker compose up -d
docker compose logs -f xpanel-subpage

گواهی​

پورت 80 روی سرور جدید آزاد است، بنابراین گواهی پیش از تنظیم nginx صادر می‌شود:

sudo certbot certonly --standalone -d sub.example.com

پیکربندی کامل nginx​

# /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 ln -sf /etc/nginx/sites-available/xlada-subpage /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot renew --dry-run

TRUST_PROXY​

TRUST_PROXY تنظیم trust proxy در Express است. آدرس واقعی کلاینت در هدر X-Forwarded-For می‌آید که جعل آن آسان است، بنابراین برنامه باید بداند به کدام هاپ‌ها اعتماد کند.

مقدارچه زمانی تنظیم شود
1پیش از صفحه دقیقاً یک پروکسی است — حالت معمول
2دو هاپ، مثلاً Cloudflare و سپس nginx
trueاعتماد به همه؛ فقط برای اشکال‌زدایی در شبکه ایزوله
falseاعتماد به هیچ‌کس، IP واقعی تشخیص داده نمی‌شود
127.0.0.1, 172.16.0.0/12اعتماد به آدرس‌ها و زیرشبکه‌های فهرست‌شده

هاپ‌های اضافی را فقط در صورتی مشخص کنید که واقعاً وجود دارند: اعتماد اضافی به کلاینت اجازه می‌دهد آدرس دیگری را در هدر جعل کند.

CUSTOM_SUB_PREFIX​

به‌طور پیش‌فرض صفحه در ریشه دامنه ارائه می‌شود. اگر مسیر لازم است، CUSTOM_SUB_PREFIX را بدون اسلش در دو طرف تنظیم کنید و همان مسیر را در پنل هم تکرار کنید:

CUSTOM_SUB_PREFIX=sub
SUB_PUBLIC_DOMAIN=sub.example.com/sub

هر دو مقدار باید یکسان باشند، وگرنه کلاینت لینکی به مسیر ناموجود دریافت می‌کند. پروکسی کردن صفحه به یک location دلخواه مجاز نیست: مسیر دقیقاً با همین متغیر تعیین می‌شود و nginx همچنان ریشه را به 127.0.0.1:3010 می‌فرستد.

تنظیم در پنل و بررسی​

  1. توکن را در REMNAWAVE_API_TOKEN در /opt/xlada-subpage/.env بنویسید و کانتینر را دوباره راه‌اندازی کنید:

    cd /opt/xlada-subpage && docker compose up -d
  2. روی سرور پنل، آدرس عمومی اشتراک را در /opt/xlada/.env مشخص کنید:

    SUB_PUBLIC_DOMAIN=sub.example.com

    مقدار — بدون https:// و بدون اسلش در انتها. اگر CUSTOM_SUB_PREFIX=sub تنظیم شده است، SUB_PUBLIC_DOMAIN=sub.example.com/sub را بنویسید.

  3. پنل را دوباره راه‌اندازی کنید تا فایل را بازخوانی کند:

    cd /opt/xlada
    docker compose up -d xpanel
# کانتینر راه‌اندازی شده و به پورت محلی گوش می‌دهد
docker compose ps
docker compose logs --tail 50 xpanel-subpage

# صفحه پاسخ می‌دهد: انتظار HTTP/2 200 داریم
curl -sI https://sub.example.com/AbCdEf1234567890 | head -n 5

# پنل باید از بیرون پاسخ دهد
curl -sI https://panel.example.com | head -n 1

پاسخ موفق شامل HTTP/2 200 و هدر profile-web-page-url است. در پنل لینک را این‌گونه بررسی می‌کنند: Users → یک کاربر را انتخاب کنید → Subscription link — آدرس باید با https://sub.example.com/ شروع شود.

هشدار

تا زمانی که در SUB_PUBLIC_DOMAIN مقداری مانند panel.example.com/api/sub نوشته شده باشد، کلاینت‌ها صفحه داخلی پنل را دریافت می‌کنند، نه صفحه‌ای که شما نصب کرده‌اید.

گام‌های بعدی​