WireGuard/AmneziaWG¶
WireGuard and AmneziaWG servers behave like any others: they sit in the same list next to VLESS/Trojan/Shadowsocks and the user picks them the same way. There is no separate mode and no extra setup. It runs on the stock Xray core (a wireguard outbound) — nothing new is required on the provider side beyond the config itself.
Option A — in a JSON subscription¶
A normal Xray config whose outbound has "protocol": "wireguard":
{
"remarks": "🇳🇱 Netherlands — WG",
"outbounds": [{
"tag": "proxy",
"protocol": "wireguard",
"settings": {
"secretKey": "<client private key>",
"address": ["10.0.0.2/32"],
"mtu": 1420,
"peers": [{
"publicKey": "<server public key>",
"preSharedKey": "<optional>",
"endpoint": "example.com:51820",
"allowedIPs": ["0.0.0.0/0", "::/0"],
"keepAlive": 25
}]
}
}]
}
The server address shown in the app comes from peers[0].endpoint. WireGuard has no vnext or servers block — the endpoint string is the only place the host lives.
AmneziaWG fields. The same outbound accepts obfuscation parameters — add them next to the usual ones. They are only for AmneziaWG; for plain WireGuard leave them out entirely (any zero value is ignored and the connection behaves exactly as before).
{
"protocol": "wireguard",
"settings": {
"secretKey": "…",
"address": ["10.66.66.17/32"],
"mtu": 1420,
"jc": 4, "jmin": 40, "jmax": 70,
"s1": 15, "s2": 25, "s3": 0, "s4": 0,
"h1": 1111111, "h2": 2222222, "h3": 3333333, "h4": 4444444,
"peers": [
{ "publicKey": "…", "endpoint": "1.2.3.4:51820", "allowedIPs": ["0.0.0.0/0"] }
]
}
}
| Field | Meaning |
|---|---|
jc |
How many junk packets to send before the handshake (4–12) |
jmin / jmax |
Junk packet size bounds, bytes (about 8–80) |
s1 / s2 |
Prefix size for the init and response packets (15–150) |
s3 / s4 |
Prefix size for the cookie and transport packets; optional — omit when the server does not set them |
h1–h4 |
Rewritten header types — without them the packets are recognised by signature. A single number or, for AmneziaWG 3.1 servers, a range "a-b" as a string ("h1": "1000-2000"); ranges are applied from Android 428, iOS/macOS/tvOS 2.0.10 (315) and desktop 203 — older builds accept numbers only |
⚠
s1–s4 and h1–h4 must be identical on the server and in the client config, otherwise the connection silently fails: the tunnel comes up but no traffic passes. jc, jmin and jmax do not have to match — each side sends its own junk.
Server version — 3.1 and older. The app is built on the AmneziaWG 3.1 branch and stays compatible with every earlier one, so there is nothing to upgrade on your side. Send only the parameters your server actually uses; anything you leave out keeps its default and is ignored.
| Added in | Parameters |
|---|---|
| 0.2.11 and earlier | jc, jmin, jmax, s1, s2, h1–h4 |
| 0.2.13 | s3, s4 |
| 0.2.16 | i1–i5 |
| 3.0 | header_protection_key, content_padding_addition, rekey_after_time, rekey_timeout, reject_after_time, keepalive_timeout, max_handshake_attempts |
| 3.1 | random_trailers, disable_cookies, ranges a-b for h1–h4 and PersistentKeepalive |
A 3.1-only parameter sent to an older server is simply not understood by it, so keep the two sides in step: configure the client with exactly what the server runs.
Option B — a wireguard:// link¶
wireguard://<base64 of a standard .conf> — the base64 holds an ordinary WireGuard configuration file, exactly the text you hand to the official client. An optional #Name anchor sets the displayed title:
The decoded payload is simply:
[Interface]
PrivateKey = <client private key>
Address = 10.0.0.2/32
DNS = 1.1.1.1
[Peer]
PublicKey = <server public key>
PresharedKey = <optional>
Endpoint = example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
This form works everywhere the other share links do: pasting into the add form, inside an encrypted crypt1 link, or as a line in the subscription body.
Build a link without writing code
Paste a .conf into the helper page — it assembles the wireguard:// link and a QR code right in your browser, with no outbound requests.
Open the link builder →
Keys are passed to the core as-is — the app does not re-encode them.
Client-side import¶
From Android 379, iOS/macOS 2.0.3 (277+) and desktop 169 the add screen takes a WireGuard/AmneziaWG config in four ways — you can hand configs to users in any of them:
- A
.conffile — the “Choose config file” button (Android, iOS, macOS, Windows, Linux; Apple TV has no file picker — it adds configs from the phone). - Raw config text — the
[Interface]…[Peer]body pasted straight into the add field, on every platform. - A link that serves the config — an https address returning the
.conftext itself. Many panels hand out configs exactly this way. - A scheme link or QR —
wireguard:///amneziawg:///awg:///wg://carrying base64 of the config, as described above.
AmneziaWG obfuscation parameters (Jc, Jmin, Jmax, S1, S2, H1–H4) are picked up from [Interface] in all four. Older builds accept the scheme link and QR only.
C. AmneziaWG — WireGuard with obfuscation¶
AmneziaWG is the same WireGuard plus obfuscation: junk packets and rewritten header types that keep DPI from recognising a WireGuard handshake.
Link schemes. wireguard://, amneziawg://, awg:// and wg:// are all accepted and parsed the same way — the payload is base64 of a standard .conf, with an optional #Name fragment.
Obfuscation parameters live in [Interface] of that .conf, next to the usual keys:
[Interface]
PrivateKey = …
Address = 10.66.66.17/32
MTU = 1420
Jc = 4 ; how many junk packets to send before the handshake
Jmin = 40 ; junk packet size, lower bound
Jmax = 70 ; junk packet size, upper bound
S1 = 15 ; init packet prefix size
S2 = 25 ; response packet prefix size
S3 = 0 ; cookie packet prefix size — optional, omit unless the server sets it
S4 = 0 ; transport packet prefix size — same
H1 = 1111111 ; rewritten header types — otherwise recognised by signature (3.1 servers may use a range: H1 = 1000-2000)
H2 = 2222222
H3 = 3333333
H4 = 4444444
[Peer]
PublicKey = …
Endpoint = 194.61.120.25:57932
AllowedIPs = 0.0.0.0/0,::/0
Leave them out and you get plain WireGuard — the app and the core behave exactly as before. The same applies field by field: send only what your server runs. Any AmneziaWG version works, 3.1 and older — see the version table in section A. 🛠️ — Build the link from a .conf — The generator takes AmneziaWG configs too — obfuscation parameters carry over as-is. Everything runs in your browser. — Link generator →
Several locations in one file. A subscription URL may return a JSON container instead of links:
{
"type": "amneziawg",
"version": 1,
"servers": [
{ "name": "Germany", "config": "<base64url of the .conf>" },
{ "name": "Netherlands", "config": "<base64url of the .conf>" }
]
}
type is accepted as amneziawg, awg, wireguard or wg. A malformed entry is skipped without taking the rest of the list down with it.
Notes for operators¶
-
IPv6 endpoints are supported, written the standard way:
[2606:4700:d0::a29f:c001]:2408. -
keepAlive(seconds) — persistent keepalive. Set it if your clients are behind NAT: without it the translation expires and the tunnel goes quiet in one direction. -
MTU in the config controls what the client sends. What the server sends is set by the MTU of the
wg0interface on the node — configure both, otherwise a path that can't carry full-size packets will pass the handshake and then stall. -
Routing on the node matters:
ip route get <client ip>must resolve viawg0. If it points at the default gateway the handshake still succeeds (the daemon answers it directly) but data never reaches the client. Looks like blocking — it isn't.