TLS — Xray-core
Xray's TLS settings live under streamSettings.tlsSettings when streamSettings.security is "tls". The struct also covers ECH and the uTLS fingerprint family. For REALITY, see REALITY — that's a parallel streamSettings.security value with its own block.
TLS settings
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
allowInsecure | bool | false | true | false | Skip server-certificate verification. Use only for testing — production configs should never set this. |
certificates | []*TLSCertConfig | [] | [TLSCertConfig] | Server certificates (inbound) or trusted-CA pinning (outbound). Multiple entries enable SNI-based selection on inbound. |
serverName | string | (inferred) | <hostname> | Expected server name. On outbound, the SNI sent and the value verified against the server's leaf cert. On inbound, the SNI used when ACME provisions a certificate. |
alpn | *StringList | ["h2", "http/1.1"] | <ALPN string> | Application-layer protocol negotiation list, offered to the peer. |
enableSessionResumption | bool | false | true | false | Enable TLS session-ticket resumption (client side). |
disableSystemRoot | bool | false | true | false | Ignore the OS root-CA bundle. When true, only `certificates` entries (in `verify` mode) are accepted. |
minVersion | string | 1.2 | 1.0 | 1.1 | 1.2 | 1.3 | Minimum acceptable TLS version. |
maxVersion | string | 1.3 | 1.0 | 1.1 | 1.2 | 1.3 | Maximum acceptable TLS version. |
cipherSuites | string | (library default) | <comma-separated cipher list> | Override the cipher suite list. Uses OpenSSL-style names (e.g. `TLS_AES_128_GCM_SHA256:...`). Note: ignored for TLS 1.3 — only 1.2 cipher choice is configurable. |
fingerprint | string | (unset) | chrome | firefox | safari | edge | 360 | qq | ios | android | random | randomized | uTLS client-hello fingerprint. Drives the entire ClientHello shape (cipher suites, extensions, signature algorithms) — overrides the explicit `cipherSuites` field. |
rejectUnknownSni | bool | false | true | false | Inbound — reject TLS connections whose SNI doesn't match any of the configured certificates. |
curvePreferences | *StringList | (library default) | X25519 | P-256 | P-384 | P-521 | X25519MLKEM768 | Override the key-exchange curve preference list. Order matters. |
masterKeyLog | string | (unset) | <file path> | SSLKEYLOGFILE-style path for capturing TLS keys (Wireshark decryption). Don't enable on production. |
pinnedPeerCertSha256 | string | (unset) | <base64 SHA-256> | Pin the peer's certificate. Outbound only — connection is rejected if the leaf certificate's SHA-256 doesn't match. |
verifyPeerCertByName | string | (unset) | <hostname> | When set, verify the peer cert's Subject CN/SAN matches this name instead of `serverName`. Useful when the SNI differs from the canonical certificate name (REALITY-style setups). |
verifyPeerCertInNames | []string | [] | [<hostname>] | List form of `verifyPeerCertByName` — any name in the list is acceptable. |
echServerKeys | string | (unset) | <base64 ECHConfigList> | Inbound — ECH server key set. |
echConfigList | string | (unset) | <base64 ECHConfigList> | Outbound — pinned ECH config list. When unset, ECH is auto-discovered via HTTPS-record DNS lookups. |
echForceQuery | string | (unset) | hkdf | dns | Force a particular ECH discovery mechanism. Empty falls back to the default cascade. |
echSockopt | *SocketConfig | (unset) | SocketConfig | Socket options applied to the ECH-discovery DNS request. |
Source: infra/conf/transport_internet.go:638-659 · pinned at v26.6.1 (94ffd50)
certificates[]
| Field | Type | Default | Allowed values | Description |
|---|---|---|---|---|
certificateFile | string | (unset) | <PEM file path> | Path to a certificate PEM file. Mutually exclusive with `certificate`. |
certificate | []string | (unset) | [<PEM line>] | Inline certificate PEM as a string array (one element per line, or the whole PEM as one element). |
keyFile | string | (unset) | <PEM file path> | Path to the private-key PEM file. |
key | []string | (unset) | [<PEM line>] | Inline private-key PEM. |
usage | string | encipherment | encipherment | verify | issue | Role of this certificate: `encipherment` (server cert), `verify` (trusted CA for peer verification), `issue` (CA used to issue per-connection certs in some forwarding modes). |
ocspStapling | uint64 | 3600 | <seconds> | How often to refresh the OCSP staple. 0 disables. |
oneTimeLoading | bool | false | true | false | Read the cert and key once at startup instead of every reload. Auto-set to true when using inline `certificate`/`key` (no path to reload from). |
buildChain | bool | false | true | false | Auto-fetch intermediate CAs to build a complete chain if the supplied PEM has only the leaf. |
Source: infra/conf/transport_internet.go:569-578 · pinned at v26.6.1 (94ffd50)
Examples
Outbound — verify against the system root store, force TLS 1.3, pin a chrome fingerprint:
json
{
"streamSettings": {
"security": "tls",
"tlsSettings": {
"serverName": "example.com",
"alpn": ["h2", "http/1.1"],
"minVersion": "1.3",
"maxVersion": "1.3",
"fingerprint": "chrome"
}
}
}Inbound — serve a Let's Encrypt cert:
json
{
"streamSettings": {
"security": "tls",
"tlsSettings": {
"serverName": "example.com",
"certificates": [
{
"certificateFile": "/etc/ssl/cert.pem",
"keyFile": "/etc/ssl/key.pem",
"usage": "encipherment",
"ocspStapling": 3600
}
],
"rejectUnknownSni": true
}
}
}Pinned-cert outbound (lock to a specific SHA-256):
json
{
"streamSettings": {
"security": "tls",
"tlsSettings": {
"serverName": "example.com",
"pinnedPeerCertSha256": "<base64-SHA256-of-leaf-cert>"
}
}
}Notes
fingerprintis uTLS — it rewrites the entire ClientHello to mimic the chosen browser. When set,cipherSuites,curvePreferences, andminVersion/maxVersionare mostly ignored because uTLS controls those itself.- TLS-1.3 ignores
cipherSuitesper the Go standard library — only TLS 1.2 cipher choice is configurable. UsecurvePreferencesto influence the key-exchange choice in TLS 1.3. - The
certificates[].usagefield is critical:encipherment— typical server certificate.verify— used as a trusted CA root on the outbound side. Pair withdisableSystemRoot: trueto ignore the OS bundle.issue— used to issue per-connection certs in some forwarding modes (MITM HTTPS pass-through).
verifyPeerCertByNamedecouples SNI from the verified name — useful in REALITY-style setups where the SNI is a public site but the actual certificate is for the Xray server.masterKeyLogis a debugging aid; the file format matches the SSLKEYLOGFILE specification that Wireshark consumes. Never enable on production.
Cross-core notes
- sing-box uses a single
tls: { ... }block embedded on every inbound and outbound. Field names are snake_case (server_name,cipher_suites), and REALITY / ECH / uTLS live as nested sub-blocks. See TLS — sing-box. - mihomo sprinkles TLS fields directly onto each proxy adapter —
tls,sni,alpn,skip-cert-verify, etc. There is no consolidated TLS block. See TLS — mihomo.
Source: infra/conf/transport_internet.go:569-659 · v26.6.1 (94ffd50)
