Files
3x-ui/docs/content/docs/en/config/ssl-certificates.mdx
T
MHSanaei 9b91f0f42e docs: vendor the documentation site into the monorepo
Fold the standalone 3x-ui-docs project (Next.js 16 + Fumadocs, deployed to
docs.sanaei.dev) into docs/ so the panel and its documentation share a single
source of truth, the way sing-box keeps its docs in-tree. The old repo becomes
redundant and can be retired.

- Import the full site under docs/ (app, components, content, lib, public,
  scripts, config). The self-contained pnpm project sits alongside the existing
  engineering notes with no filename collisions.
- Re-point "Edit on GitHub" links from MHSanaei/3x-ui-docs to this repo's
  docs/content/docs path (docs/lib/shared.ts, docs/app/.../page.tsx).
- Add docs-ci.yml and docs-deploy.yml under .github/workflows/, scoped to
  docs/** and run with working-directory: docs, since GitHub only runs
  workflows from the repo-root .github/. deploy-static.yml's GitHub Pages
  publish (CNAME docs.sanaei.dev) carries over unchanged.

Follow-up (outside this commit): attach the docs.sanaei.dev custom domain to
this repository's Pages (or set the Vercel project's root directory to docs),
confirm the site is live from the monorepo, then delete MHSanaei/3x-ui-docs.
2026-07-07 23:07:14 +02:00

182 lines
6.9 KiB
Plaintext

---
title: SSL Certificates
description: Get and renew TLS certificates for the 3x-ui panel and inbounds — with the x-ui ACME menu (domain or bare IP), a Cloudflare DNS-01 wildcard, or manual Certbot.
icon: ShieldCheck
---
A TLS certificate lets you serve the **panel** over HTTPS (so your login and API
traffic are encrypted) and terminate TLS on **inbounds** (VLESS-TLS, Trojan,
Shadowsocks-TLS, and friends). There are three ways to obtain one:
- **The `x-ui` menu** — built-in [ACME](https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment)
client. Easiest for a single domain or a bare IP.
- **Cloudflare DNS-01** — also from the menu; needed for **wildcard** certs or
when port 80 is blocked / the server sits behind Cloudflare's proxy.
- **Manual Certbot** — if you'd rather manage `acme.sh`/Certbot yourself.
<Callout type="info">
If you put the panel behind Nginx or Caddy, let the proxy handle the
certificate instead — see [Reverse proxy](/docs/operations/reverse-proxy).
[REALITY](/docs/config/reality) inbounds need **no** certificate at all; they
borrow a real site's TLS. This page is for the panel and for classic TLS
inbounds.
</Callout>
## The `x-ui` SSL menu (Let's Encrypt)
Run `x-ui` and choose **`20` — SSL Certificate Management**. It drives
[acme.sh](https://github.com/acmesh-official/acme.sh) and offers:
| Option | What it does |
| ------------------------------ | ------------------------------------------------------------------- |
| Get SSL (Domain) | Issue a certificate for a domain via HTTP validation. |
| Get SSL for IP Address | Issue a short-lived (6-day, auto-renewing) cert for a **bare IP**. |
| Revoke | Revoke an existing certificate. |
| Force Renew | Renew now, before expiry. |
| Show Existing Domains | List certificates already on the server. |
| Set Cert paths for the panel | Point the panel's TLS at an issued cert (sets the fields for you). |
### Issue a certificate for a domain
<Steps>
<Step>
### Point the domain at the server
Create an `A` (and/or `AAAA`) record for your domain that resolves to this
server's public IP. Validation fails until DNS has propagated.
</Step>
<Step>
### Free up port 80
HTTP validation needs **port 80** reachable from the internet and not already in
use. Stop anything bound to it for the duration, and allow it through the
[firewall](/docs/reference/ports-firewall).
</Step>
<Step>
### Run the issuer
`x-ui` → `20` → **Get SSL (Domain)**, then enter the domain. acme.sh requests
the certificate and saves it under `/root/cert/<domain>/` as `fullchain.pem`
(the certificate chain) and `privkey.pem` (the private key).
</Step>
<Step>
### Wire it into the panel
Choose **Set Cert paths for the panel** to fill in `webCertFile` and
`webKeyFile` and restart the panel, or set them yourself in
[Panel Settings](/docs/config/panel#tls). The panel serves HTTPS as soon as both
are set.
</Step>
</Steps>
### Issue a certificate for a bare IP
No domain? Choose **Get SSL for IP Address** to obtain a short-lived
certificate (valid ~6 days, renewed automatically) bound to the server's IP.
Useful for reaching the panel over HTTPS before you've set up a domain.
## Cloudflare (DNS-01 wildcard)
DNS validation proves you control the domain by creating a TXT record instead of
answering on port 80 — so it works **behind Cloudflare's proxy**, on servers
where port 80 is blocked, and for **wildcard** certificates (`*.example.com`).
Your domain's DNS must be managed by Cloudflare, and you need one of:
- a **scoped API token** with the `Zone:DNS:Edit` permission (recommended), or
- your account **email + Global API Key**.
<Steps>
<Step>
### Create a scoped API token
In the Cloudflare dashboard go to **My Profile → API Tokens →
[Create Token](https://dash.cloudflare.com/profile/api-tokens)**, pick the
**Edit zone DNS** template, scope it to the zone you're issuing for, and create
it. Copy the token — it's shown only once.
</Step>
<Step>
### Run the Cloudflare issuer
`x-ui` → **`21` — Cloudflare SSL Certificate**. When asked, choose **`t`** for an
API token (the default) or **`g`** for the Global API Key, then enter your
domain (and, for the Global API Key, your account email and key). acme.sh creates
the TXT record, validates, and cleans it up.
</Step>
<Step>
### Point the panel at it
As with the domain flow, use **Set Cert paths for the panel** (menu `20`) or set
`webCertFile` / `webKeyFile` in [Panel Settings](/docs/config/panel#tls).
</Step>
</Steps>
<Callout type="info">
Prefer a scoped token over the Global API Key — it only grants DNS edits on the
zone you choose, so a leak can't touch the rest of your Cloudflare account.
</Callout>
## Manual (Certbot)
If you'd rather not use the menu, issue a certificate with Certbot's standalone
plugin (again, this needs port 80 free and the domain resolving to the server):
```bash
apt-get install certbot -y
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
certbot renew --dry-run
```
Certbot writes the certificate to `/etc/letsencrypt/live/yourdomain.com/`
(`fullchain.pem` and `privkey.pem`). Point the panel at those two files in
[Panel Settings](/docs/config/panel#tls), and set up renewal — `certbot renew`
runs on a systemd timer by default.
## Using the certificate
- **Panel** — set `webCertFile` (the full chain) and `webKeyFile` (the private
key) in [Panel Settings](/docs/config/panel#tls). Both must be set for the
panel to switch to HTTPS. Menu option **`11` — View Current Settings** prints
the paths currently in use.
- **Inbounds** — when you enable TLS on an inbound, reference the same
certificate and key files (or paste their contents) in the inbound's TLS
settings. See [Inbounds](/docs/config/inbounds) and
[Transports](/docs/config/transports).
<Callout type="warn">
Certificates expire (Let's Encrypt: 90 days; IP certs: ~6 days). The menu and
Certbot both renew automatically, but the panel keeps reading the **files** at
their fixed paths — so renew **in place** rather than moving the files, and the
panel picks up the new cert on its next restart. **Force Renew** (menu `20`)
triggers a renewal on demand.
</Callout>
## Next steps
<Cards>
<Card
title="Panel Settings"
href="/docs/config/panel#tls"
description="webCertFile / webKeyFile and the rest of the web-server settings."
/>
<Card
title="Reverse proxy"
href="/docs/operations/reverse-proxy"
description="Let Nginx or Caddy terminate TLS for you instead."
/>
<Card
title="REALITY"
href="/docs/config/reality"
description="Stealth TLS for inbounds — no certificate required."
/>
</Cards>