Skip to content

Censorship bypass

WHY IT MATTERS

D. PI systems often block by SNI (the destination domain) read from the TLS ClientHello — the first packet of every TLS/Reality handshake. Fragmentation splits that ClientHello into many tiny TCP segments with micro-delays, so DPI can't reassemble the SNI → can't see the domain → can't cut by domain. It runs entirely on the stock Xray core (a freedom outbound with fragment settings) — with no extra server or infrastructure.

The four parameters below solve different problems and are often used together.

✓ The app configures everything for you. You do NOT need to add a fragment outbound manually: when enabled, the app inserts it and routes all direct TLS/Reality traffic through it on ANY config, leaving multi-hop chains untouched — only the hop the DPI sees gets fragmented.

08.1 · s-fragment — TLS fragmentation

Enabling — the s-fragment

Value Effect
absent / off Off (an absent header leaves the previous value unchanged)
on On, with default parameters
packets=tlshello;length=50-100;interval=10-20;maxsplit=100-200 On, with these parameters

Sent as the HTTP response header s-fragment: or as a body comment line #s-fragment: packets=tlshello;length=50-100;interval=10-20.

🔒 Requires an active Provider ID. Like any provider-controlled field, s-fragment is honoured only while your Provider ID is active. An inactive or unknown provider can't enable fragmentation on users' devices.

Parameters

Parameter Format · default · description
packets tlshello or N-M · default tlshelloWhich packets to fragment. tlshello — only the TLS ClientHello (carries the SNI); recommended, minimal overhead. 1-3 — the first 1–3 outbound packets.
length min-max (bytes) · default 50-100Fragment size, random within the range. Smaller = harder to reassemble but more overhead.
interval min-max (ms) · default 10-20Delay between fragments, random within the range. Jitter hinders buffering and reassembly.
maxsplit min-max · default unsetOptional (newer cores only). Caps the number of fragments; without it, unlimited. Older cores ignore it.

Defaults suit most networks. Tune for a specific DPI: harder blocking → smaller length and/or larger interval; slower speed → larger length and smaller interval.

Parameter names are case-insensitive (maxsplit and maxSplit are the same key), and the packets key is plural — packets=, not packet=. packet= belongs to s-noise, where it means the payload.

⚙️Users can enable it themselves

A user can also enable fragmentation and edit the same parameters manually — in the app: Settings → Bypass → TLS fragmentation, independently of the provider. When enabled manually, their parameters take priority over the header.

08.2 · s-noise — noise packets

Junk traffic sent ahead of the connection so DPI can't recognise its start. It works alongside fragmentation — they share the same egress, and together they cover both the start of the connection and what's inside it.

Values: on / 1 uses the defaults (rand, packet 50-100, delay 10-20); off / 0 disables it. Full form is type=rand;packet=50-100;delay=10-20, or positional rand,50-100,10-20; type accepts rand, str or hex.

Both spellings of the header are accepted — s-noise and s-noises.

08.3 · s-resolve — pre-resolving the server address

Resolves the server address over DoH before the tunnel comes up. Use it where the local DNS returns a forged answer for your node's domain: without it the client gets a wrong IP and simply never connects.

Values: on / 1 uses the default resolver (doh.ffmuc.net); off / 0 disables it; a URL sets your own.

Where even the resolver's own domain is poisoned, add a bootstrap IP with ;ip=… — for example https://doh.ffmuc.net/dns-query;ip=5.1.66.255. The original domain stays as SNI, so TLS/Reality still validate. If the resolver doesn't answer within a second and a half the normal lookup is used — the parameter can only help, never block the connection.

Several resolvers can be listed, separated by commas, each with its own bootstrap IP — they are tried in order until one answers. A single address behaves exactly as before: it is the same format without commas.

s-resolve: https://a.example/dns-query;ip=1.2.3.4, https://b.example/dns-query;ip=5.6.7.8

A list beats the app's built-in set: you name the resolvers you know work on your users' networks, which the built-in list cannot know. It also can't be switched on by a header at all — only by the user's own toggle.

08.4 · s-dns — DNS-over-HTTPS in the tunnel

Encrypts ordinary DNS queries inside the tunnel. Values: on / 1 enables the built-in default resolver (doh.ffmuc.net); a URL such as https://doh.ffmuc.net/dns-query sets your own; off / 0 disables it. Resolved DNS travels through the tunnel, not the physical network.

Order of application

Provider parameters always outrank the user's own settings, off included. When the user configures it themselves, the order is: fragmentation → noise → DoH.

REALITY: disable post-quantum key exchange

Available starting with iOS/macOS/tvOS 2.0.39 (375), Android 2.0.6 (502), Windows/Linux 2.0.2 (258). Off by default. Open Settings → Censorship bypass → REALITY: disable post-quantum key exchange. Reconnect to apply changes.

Why this exists

Modern Chrome, Firefox and Safari presets can include the hybrid X25519MLKEM768 group with a 1216-byte key share in TLS ClientHello. This often makes ClientHello span more than one TCP segment. On some networks, DPI or intermediate equipment may interfere with that handshake: the VPN appears connected but requests stall, or connections time out. This is only one possible cause of stalls.

When enabled, SMProxy keeps the selected fingerprint, removes only X25519MLKEM768 from supported_groups and key_share, and rebuilds ClientHello, matching sing-box's REALITY client behavior. Other preset extensions remain. This does not select an older hellochrome_120 fingerprint and is independent of TLS fragmentation. Presets without ML-KEM, such as ios, need no such modification.

Only outbound REALITY connections are affected, including REALITY outbounds in chains. Ordinary TLS, WireGuard/AmneziaWG, Hysteria2 and TUIC are unchanged. REALITY ML-DSA authentication is not disabled. Conventional X25519 key exchange remains, but hybrid post-quantum protection for key agreement is disabled. ClientHello size also depends on the fingerprint, SNI and other extensions: a single TCP segment and successful bypass are not guaranteed.

Subscription control — s-reality-disable-pq

No active Provider ID is required. Applies only to servers belonging to that subscription, never to other subscriptions.

Value Effect
true, 1, on Remove the REALITY ML-KEM group and key share
false, 0, off Keep the original preset, even when the local toggle is on
Absent Inherit the local toggle (off by default)

Values are case-insensitive; quoted "true" and "false" also work. Invalid values do not enable the setting. Standard crypt1/… header-value decryption is supported.

HTTP response header on the subscription URL:

s-reality-disable-pq: true

First lines of a text file containing share links (local import or server response):

#s-reality-disable-pq: true
vless://UUID@vpn.example.com:443?security=reality&fp=chrome&...

Subscription URL or direct-link query parameter:

https://panel.example.com/subscription?s-reality-disable-pq=true
vless://UUID@vpn.example.com:443?security=reality&fp=firefox&s-reality-disable-pq=true&...

Source precedence: HTTP header → file/body comment → URL query → local toggle. Explicit false has the same priority as true. Removing the subscription value restores the local setting after refresh unless a separately managed provider header still supplies it. For a full JSON configuration, use an HTTP header or URL query rather than putting a comment before JSON. Our engine receives streamSettings.realitySettings.disablePq as a JSON boolean.

Verification

Reconnect to the same server. The connection log records whether disabling PQ is enabled or disabled, its source (subscription or local settings), and the number of affected REALITY outbounds. Toggle changes are logged with a reconnect reminder. Zero outbounds means the current configuration was not affected.

Compare on the same network and server; first disable TLS fragmentation and change only this setting. Server-side tcpdump shows TCP sizes and segmentation; the application log shows the effective setting and handshake errors. If connections still stall without PQ, further investigation is required: a small ClientHello alone proves neither the network nor the server is healthy.