Separate server
Separate server installation means that you will install subscription page on a different server from Remnawave Panel.
Step 1 - Prepare .env fileβ
Make this changes on the server where Remnawave Panel is installed.
Edit the /opt/remnawave/.env file and change SUB_PUBLIC_DOMAIN to your subscription page domain name.
cd /opt/remnawave && nano .env
Change SUB_PUBLIC_DOMAIN to your subscription page domain name. Domain name must be without http or https.
SUB_PUBLIC_DOMAIN=subscription.domain.com
Don't forget to restart Remnawave Panel container:
cd /opt/remnawave && docker compose down remnawave && docker compose up -d && docker compose logs -f
Step 2 - Create docker-compose.yml fileβ
This step and next steps should be made on another server!
Make sure you have Docker installed on the server where you will install the subscription page.
mkdir -p /opt/remnawave/subscription && cd /opt/remnawave/subscription && nano docker-compose.yml
services:
remnawave-subscription-page:
image: remnawave/subscription-page:latest
container_name: remnawave-subscription-page
hostname: remnawave-subscription-page
restart: always
env_file:
- .env
ports:
- '127.0.0.1:3010:3010'
networks:
- remnawave-network
networks:
remnawave-network:
name: remnawave-network
driver: bridge
external: false
Now create .env file:
mkdir -p /opt/remnawave/subscription && cd /opt/remnawave/subscription && nano .env
Create API token in Remnawave dashboard. Remnawave Settings β API Tokens.
Paste the following content into the .env file:
APP_PORT=3010
REMNAWAVE_PANEL_URL=https://remnawave.panel.com
REMNAWAVE_API_TOKEN=API_TOKEN_FROM_REMNAWAVE
Replace remnawave.panel.com with your panel domain name.
Full .env file reference
APP_PORT=3010
### Remnawave Panel URL, can be http://remnawave:3000 or https://panel.example.com
REMNAWAVE_PANEL_URL=https://panel.example.com
REMNAWAVE_API_TOKEN=API_TOKEN_FROM_REMNAWAVE
# Serve at custom root path, for example, this value can be: CUSTOM_SUB_PREFIX=sub
# Do not place / at the start/end
CUSTOM_SUB_PREFIX=
# Support Marzban links
MARZBAN_LEGACY_LINK_ENABLED=false
MARZBAN_LEGACY_SECRET_KEY=
# If you use "Caddy with security" addon, you can place here X-Api-Key, which will be applied to requests to Remnawave Panel.
CADDY_AUTH_API_TOKEN=
# If you use Cloudflare Zero Trust to protect your panel, please use this variables to bypass the security.
CLOUDFLARE_ZERO_TRUST_CLIENT_ID=
CLOUDFLARE_ZERO_TRUST_CLIENT_SECRET=
# Express `trust proxy` setting, used so the real client IP is resolved from
# the trusted hop and cannot be spoofed via X-Forwarded-For. Accepts:
# - "true" / "false" -> trust all / none
# - a non-negative integer (e.g. 1, 2) -> number of trusted hops
# - a comma-separated list of preset names -> loopback, linklocal, uniquelocal
# and/or IP addresses / CIDR subnets -> e.g. 127.0.0.1, 172.16.0.0/12
# See https://expressjs.com/en/guide/behind-proxies/
TRUST_PROXY=1
About TRUST_PROXYβ
TRUST_PROXYThe subscription page runs behind a reverse proxy (Caddy / Nginx), so the client connects to the proxy, not directly to the app. To know the real client IP, the app reads the X-Forwarded-For header β but that header can be spoofed by the client unless the app is told which hops to trust.
TRUST_PROXY is the Express trust proxy setting and controls exactly that. Accepted values:
true/falseβ trust all proxies / trust none.- a non-negative integer (e.g.
1,2) β number of trusted hops between the app and the client. - a comma-separated list of preset names (
loopback,linklocal,uniquelocal) and/or IP addresses / CIDR subnets (e.g.127.0.0.1, 172.16.0.0/12).
For the standard single reverse proxy in front of the page, the default TRUST_PROXY=1 is correct. Increase the hop count only if you have additional proxies in front (e.g. Cloudflare β Nginx β app).
Step 3 - Start the containerβ
docker compose up -d && docker compose logs -f
Step 4 - Configure reverse proxyβ
Remnawave and its components does not support being server on a sub-path. (e.g. location /subscription {)
It has to be served on the root path of a domain or subdomain.
For custom path, you can use the CUSTOM_SUB_PREFIX parameter.
Caddyβ
Caddy configuration
Create a file called Caddyfile in the /opt/remnawave/caddy directory.
mkdir -p /opt/remnawave/caddy && cd /opt/remnawave/caddy && nano Caddyfile
Paste the following configuration.
Please, replace REPLACE_WITH_YOUR_DOMAIN with your subscription page domain name.
Review the configuration below, look for red highlighted lines.
https://SUBSCRIPTION_PAGE_DOMAIN {
reverse_proxy * http://remnawave-subscription-page:3010
}
:443 {
tls internal
respond 204
}
Please replace SUBSCRIPTION_PAGE_DOMAIN with your domain name.
Review the configuration below, look for green highlighted lines.
Create docker-compose.ymlβ
Create a docker-compose.yml file in the /opt/remnawave/caddy directory.
cd /opt/remnawave/caddy && nano docker-compose.yml
Paste the following configuration.
services:
caddy:
image: caddy:2.9
container_name: 'caddy'
hostname: caddy
restart: always
ports:
- '0.0.0.0:443:443'
- '0.0.0.0:80:80'
networks:
- remnawave-network
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy-ssl-data:/data
networks:
remnawave-network:
name: remnawave-network
driver: bridge
external: true
volumes:
caddy-ssl-data:
driver: local
external: false
name: caddy-ssl-data
Start the containerβ
docker compose up -d && docker compose logs -f -t
Step 5 - Usageβ
The subscription page will be available at https://subdomain.panel.com/<shortUuid>.
Configuring subscription page (optional)β
You can customize the subscription page in the Subpage Builder in Remnawave Dashboard. This allows you to:
- Add support for different VPN apps
- Customize text and instructions in multiple languages
- Add your own branding (logo, company name, support links)
- Configure which apps appear as "featured"