21 Configuration
Sanaei edited this page 2026-06-03 00:45:23 +02:00

Getting SSL

ACME

To manage SSL certificates using ACME:

  1. Ensure your domain is correctly resolved to the server.

  2. Run the x-ui command in the terminal, then choose SSL Certificate Management.

  3. You will be presented with the following options:

    • Get SSL (Domain): Obtain SSL certificates for a domain.
    • Revoke: Revoke existing SSL certificates.
    • Force Renew: Force renewal of SSL certificates.
    • Show Existing Domains: Display all domain certificates available on the server.
    • Set Cert paths for the panel: Specify the certificate for your domain to be used by the panel.
    • Get SSL for IP Address: Issue a short-lived (6-day, auto-renewing) certificate for a bare IP address.

Certbot

To install and use Certbot:

apt-get install certbot -y
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
certbot renew --dry-run

Cloudflare

The management script includes a built-in SSL certificate application for Cloudflare (uses DNS validation, so it also works for wildcard certificates and servers behind Cloudflare's proxy). To use it you need:

  • A Cloudflare API Token (recommended) scoped to Zone:DNS:Edit for your zone, or your Cloudflare registered email + Global API Key
  • The domain must be managed by Cloudflare (its nameservers point to Cloudflare)

Run the x-ui command in the terminal, then choose Cloudflare SSL Certificate. The script will ask whether you are using an API Token (t, default) or Global API Key (g), then prompt for the domain.

How to create a scoped API Token (recommended):

  1. Visit Cloudflare API Tokens.
  2. Click Create Token → Edit zone DNS template, scope it to your zone, and create it.
  3. Copy the token and paste it into the script when prompted.

How to get the Global API Key (alternative):

  1. Visit the link: Cloudflare API Tokens.
  2. Click on "View Global API Key" (see the screenshot below):

  1. You may need to re-authenticate your account. After that, the API Key will be shown (see the screenshot below):

When using the Global API Key, enter your domain name, email, and API KEY. The flow is as follows:

Available environment variables

These are read at startup. On a systemd install you can set them in the service environment file (/etc/default/x-ui, /etc/conf.d/x-ui, or /etc/sysconfig/x-ui, depending on distro); for Docker, pass them with -e / the environment: block.

XUI_DB_TYPE

  • Description: Database backend
  • Type: string
  • Acceptable values: sqlite | postgres
  • Default value: sqlite

XUI_DB_DSN

  • Description: PostgreSQL connection string (used when XUI_DB_TYPE=postgres)
  • Type: string
  • Default value: — (empty)
  • Example: postgres://xui:password@127.0.0.1:5432/xui?sslmode=disable

XUI_DB_FOLDER

  • Description: Path to the folder holding the SQLite database file
  • Type: string
  • Default value: /etc/x-ui

XUI_DB_MAX_OPEN_CONNS

  • Description: Maximum number of open database connections
  • Type: integer
  • Default value: 25 (PostgreSQL) / 8 (SQLite)

XUI_DB_MAX_IDLE_CONNS

  • Description: Maximum number of idle database connections
  • Type: integer
  • Default value: 25 (PostgreSQL) / 4 (SQLite)

XUI_BIN_FOLDER

  • Description: Path to the folder with xray-core, geosite & geoip databases
  • Type: string
  • Default value: bin

XUI_LOG_LEVEL

  • Description: Default log level
  • Type: string
  • Acceptable values: debug | info | notice | warning | error
  • Default value: info

XUI_LOG_FOLDER

  • Description: Path to the logs
  • Type: string
  • Default value: /var/log/x-ui

XUI_DEBUG

  • Description: Whether debug mode should be enabled (forces the log level to debug)
  • Type: boolean
  • Default value: false

XUI_ENABLE_FAIL2BAN

  • Description: Should fail2ban enforce per-client IP limits
  • Type: boolean
  • Default value: true

XUI_SKIP_HSTS

  • Description: Skip sending the Strict-Transport-Security (HSTS) header — useful when terminating TLS at a reverse proxy
  • Type: boolean
  • Default value: false

Reverse Proxy

Nginx

To configure the reverse proxy, add the following paths to your nginx config

location / {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Range $http_range;
    proxy_set_header If-Range $http_if_range;

    proxy_redirect off;
    proxy_pass http://127.0.0.1:2053;
}

Note

The URL in the panel settings needs to end with /.

For the subscriptions (the default subscription port is 2096)

location /sub {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Range $http_range;
    proxy_set_header If-Range $http_if_range;

    proxy_redirect off;
    proxy_pass http://127.0.0.1:2096;
}

Note

Ensure that the "URI Path" in the /sub panel settings is the same.

Caddy

Important

A huge thanks to @Gill-Bates for providing the config

Important

This configuration will work when the "WebSocket" transport is set inbound

Before configuring caddyfile, make sure that the following parameters are set in the panel setup

Screenshot

After customizing the panel, modify the caddyfile as follows

vpn.example.com {

    encode gzip

    # TLS 1.3 mandatory!
    tls {
        protocols tls1.3
    }

    # Protect your GUI with Basic Auth
    route /admin* {
        basic_auth {
            admin ******
        }
        reverse_proxy xx.xx.xx.xx:2053
    }

    # Obfuscate the Endpoint
    route /api/v1* {
        @websockets {
            header Connection *Upgrade*
            header Upgrade websocket
        }
        reverse_proxy @websockets xx.xx.xx.xx:54321
        respond "Forbidden" 403
    }

    # Security Header
    header {
        header_up Authorization { >Authorization }
        header_up Content-Type { >Content-Type }
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options nosniff
        X-Frame-Options SAMEORIGIN
        Referrer-Policy strict-origin-when-cross-origin
        -Server
        -X-Powered-By
    }

    # Fallback
    respond "Not found!" 404
}

The following data must be replaced in the config:

  • vpn.example.com -> your domain.
  • admin ***** -> replace the asterisks with your password.

If you do not need HTTP Auth, remove the following lines

basic_auth {
    admin ******
}
  • reverse_proxy xx.xx.xx.xx -> replace the xx.xx.xx.xx with your IP
  • reverse_proxy @websockets xx.xx.xx.xx:54321 -> replace 54321 with your inbound port

Setting Fail2Ban

The bundled Fail2ban integration enforces per-client IP limits: when a client exceeds its allowed number of simultaneous IPs, the offending address is temporarily banned.

Note

The IP limit won't work correctly when using an IP Tunnel.

To set it up, run the x-ui command in the terminal and choose IP Limit Management. You will see the following options:

  • Install Fail2ban and configure IP Limit: Install Fail2ban and set up the 3x-ui jail.
  • Change Ban Duration: Adjust how long bans last.
  • Unban Everyone: Lift all current bans.
  • Ban Logs: View the ban history.
  • Ban an IP Address: Manually ban an IP.
  • Unban an IP Address: Manually unban an IP.
  • Real-Time Logs: Follow the live Fail2ban log.
  • Service Status: Check the status of fail2ban.
  • Service Restart: Restart the fail2ban service.
  • Uninstall Fail2ban and IP Limit: Remove Fail2ban and its configuration.

Then enable the access log so Fail2ban can read client connections: in the panel go to Xray Configs, set the log → Access log path to ./access.log, save, and restart Xray.

Tip

On Docker, Fail2ban is bundled and enabled by default (XUI_ENABLE_FAIL2BAN=true). It needs the NET_ADMIN (and NET_RAW for IPv6) capabilities to apply bans with iptables — see the Docker installation section.


API Documentation

3X-UI ships an interactive Swagger UI built into the panel. Open the panel and click API Docs in the sidebar to browse every endpoint, see request/response schemas, and try calls live against your server.

  • The OpenAPI 3 specification is served at <your-panel-url>/panel/api/openapi.json.
  • API requests authenticate with your panel session cookie, or with an API token created under Settings.
  • API Documentation (Postman) — community-maintained collection.

Geosites

What is it?

The Geosites in Xray-core play a key role in traffic routing, enabling flexible control over traffic distribution based on the geographical location of IP addresses and domains. Here are their main files:

  • geoip.dat contains a database of IP addresses classified by country (e.g., geoip:cn for China or geoip:private for private networks). This allows:

    • Redirecting traffic for specific countries (e.g., Chinese IPs via direct, others via proxy).

    • Blocking or allowing access to IPs from certain regions.

  • geosite.dat includes domain lists grouped by categories (e.g., geosite:cn for Chinese domains, geosite:google for Google services). This is used for:

    • Granular traffic control (e.g., ad domains → block, streaming → proxy).

Sources

3X-UI uses multiple geofiles sources for flexible traffic routing:

Repository Files Available geosites
Loyalsoldier/v2ray-rules-dat geoip.dat
geosite.dat
View
🇮🇷 chocolate4u/Iran-v2ray-rules geoip_IR.dat
geosite_IR.dat
View
🇷🇺 runetfreedom/russia-v2ray-rules-dat geoip_RU.dat
geosite_RU.dat
View

Updating geofiles

  1. Open panel and click to Xray version

image

  1. Open Geofiles dropdown and update the needed geofile

image

Custom GeoSite / GeoIP DAT sources

Administrators can add custom GeoSite and GeoIP .dat files from URLs in the panel (same workflow as updating built-in geofiles). Files are stored under the same directory as the Xray binary (XUI_BIN_FOLDER, default bin/) with deterministic names: geosite_<alias>.dat and geoip_<alias>.dat.

Routing: Xray resolves extra lists using the ext: form, for example ext:geosite_myalias.dat:tag or ext:geoip_myalias.dat:tag, where tag is a list name inside that DAT file (same pattern as built-in regional files such as ext:geoip_IR.dat:ir).

Reserved aliases: Only for deciding whether a name is reserved, the panel compares a normalized form of the alias (strings.ToLower, -_). User-entered aliases and generated file names are not rewritten in the database; they must still match ^[a-z0-9_-]+$. For example, geoip-ir and geoip_ir collide with the same reserved entry.