Skip to content

feature: webhook property in rules#5722

Open
kastov wants to merge 2 commits intoXTLS:mainfrom
kastov:feat/webhook
Open

feature: webhook property in rules#5722
kastov wants to merge 2 commits intoXTLS:mainfrom
kastov:feat/webhook

Conversation

@kastov
Copy link
Contributor

@kastov kastov commented Feb 23, 2026

Many administrators use Xray's built-in routing capabilities to block certain traffic, such as BitTorrent. Tools like Xray Torrent Blocker exist for this purpose — they work by monitoring log files, parsing tags, and applying the necessary actions. While this approach works well, reading log files is far from an ideal solution and comes with a number of inherent limitations.

This PR introduces a new rule property — webhook. The request can be sent either over regular HTTP(S) or via a Unix Domain Socket. Emitted when rule matched.

Request Structure

{
  "email": "2",                        // string | null
  "level": null,                       // number | null
  "protocol": "tls",                   // string | null
  "network": "tcp",                    // string
  "source": "tcp:127.0.0.1:54203",     // string | null
  "destination": "tcp:dns.google:443", // string
  "routeTarget": null,                 // string | null
  "originalTarget": "tcp:8.8.8.8:443",// string | null
  "inboundTag": "VLESS_TCP",           // string | null
  "inboundName": "vless",              // string | null
  "inboundLocal": "tcp:192.168.108.1:443", // string | null
  "outboundTag": "NTFY_BLOCK",  // string | null
  "ts": 1771886901                     // number
}

Note: The PR intentionally uses pointer types so that all fields are always present in the request body. If any data is unavailable, the corresponding field is sent as null. This significantly simplifies parsing on the receiving end.

Usage Example

The following example demonstrates how to use this feature with Xray's built-in routing:

Routing rule:

{
  "protocol": ["bittorrent"],
  "outboundTag": "NTFY_BLOCK",
  "webhook": {
            "url": "http://127.0.0.1:8080/api/webhook",
            "deduplication": 10,
            "headers": {
              "X-API-Key": "your-secret-key"
            }
      }
}

Documentation

A companion PR has also been submitted to add documentation for the feature described above. The documentation has been translated into all supported languages. Please note that the Chinese translation was produced with the assistance of AI and should be reviewed by a native speaker.

Linked PR: XTLS/Xray-docs-next#823

@Fangliding
Copy link
Member

Fangliding commented Feb 24, 2026

xray的torrent嗅探极其原始很容易被绕过
添加这个出站的用途很明显在用户发送服务提供者不想要的请求的时候触发回调记录用户数据用以记录请求信息/警告/封禁账号等用途 总感觉这看起来更像是一个审查系统而不是一个反审查系统(

@kastov
Copy link
Contributor Author

kastov commented Feb 24, 2026

xray的torrent嗅探极其原始很容易被绕过

That is entirely true, and I see no reason to deny it. However, even such a primitive protection — combined with Xray-Torrent-Blocker (linked above) — can work together quite effectively. Xray reports the information (currently only through the log file), and Xray-Torrent-Blocker applies a temporary IP ban for a specified duration. This way, even with a basic detection mechanism, the risks can be meaningfully minimised.

添加这个出站的用途很明显在用户发送服务提供者不想要的请求的时候触发回调记录用户数据用以记录请求信息/警告/封禁账号等用途 总感觉这看起来更像是一个审查系统而不是一个反审查系统(

All the data included in the webhook request is already available to the administrator through the standard access.log. Following this logic, access.log can also be used as "spy tool" — since it allows performing exactly the same actions as the webhookSource IP, Destination IP/domain, routing tag, timestamp: Xray-Log-Analyzer, xray-logger.
Let me give a concrete example. Suppose I want to disable logging entirely and set "access": "none". This immediately creates a problem: tools like Xray-Torrent-Blocker stop working, since they rely on reading the log file – monitoring it in real time for the -> TORRENT string, parsing the IP address, and blocking it via nftables/iptables/ufw for a specified duration.
At the same time, there is no way to configure logging selectively — so that only entries with the TORRENT tag are written to the file. It's all or nothing.
This is exactly where the webhook solves the problem elegantly: instead of enabling full logging just for a single use case, we simply receive a notification at the right moment. Yes, the webhook carries the same data as access.log – but only for a specific event, without recording all traffic indiscriminately. As things stand, if we need even a minimal reaction to certain types of connections, we are forced to enable full logging — even if we don't want to store anything at all.


we already have a real surveillance tool – IP Map...

@gfw-killer
Copy link

At the same time, there is no way to configure logging selectively — so that only entries with the TORRENT tag are written to the file. It's all or nothing.

It seem to be very useful to categorize logs and disable/enable them and write each category logs to a different file

Another way to mitigate the torrent and also other abuse reports, is to whitelist protocols (http/tls/quic/dns/ntp/etc.) and games by asn/cidr and disallow others or route them to a bulletproof server, it allows your users to torrent too
but currently Xray-core can only detect http/tls/quic/bittorrent (and detects them all even if you don't need it)

@RPRX
Copy link
Member

RPRX commented Feb 24, 2026

用户跑 BT 的话会导致 VPS 被服务商封掉,且涉及版权问题,所以我觉得出发点没啥问题

但是比起来加个出站不如完善日志系统,使它支持选择性日志和多重日志(同一条日志写两个地方),主动请求倒是没必要

@Fangliding
Copy link
Member

Fangliding commented Feb 24, 2026

担心用户跑bt直接导到黑洞就行了(假如相信那个sniffer)

以及

reading log files is far from an ideal solution and comes with a number of inherent limitations

恐怕这个pr本身就是为了解决处理log作为一个纯文本系统和面板自动化集成的不便

改log一边一个需求没完没了了 除了log level没有看到什么很理想的分类 像chrome的日志稍微启动一下就导出来高大全一堆然后自己往里翻

@Jolymmiles
Copy link
Contributor

Is here any problem with webhook @Fangliding ?

@Jolymmiles
Copy link
Contributor

I don't really see any problem with this. Your suggestion to rewrite all log files feels like overkill - like using a cannon to kill a sparrow. But what everyone seems to be overlooking is the actual issue: Xray struggles to write logs to file when the kernel is under heavy load.

@RPRX
Copy link
Member

RPRX commented Feb 24, 2026

@Fangliding 我觉得这是整个软件的行为模式的问题,可以改 log 系统或者 Xray 自己存一下连接信息然后加 API(比如 Clash 就是这样做的),这样更通用,可维护性也更高,而不是去主动兼容各种下游,这个头一开工作量就没完没了了

不改 log 系统的话就给 Xray 加个存连接信息的模块吧

@Fangliding
Copy link
Member

他们要的信息已经有了 就是context里那个routing info

@RPRX
Copy link
Member

RPRX commented Feb 24, 2026

现在不是还没有 API 能查到连接级信息吗

我觉得加个出站的话不如直接改 blackhole 出站,但是这么设计的话局限性也很大,不如改 routing 更通用

然后就还是那个问题,到底是选主动请求还是被动查询

@kastov
Copy link
Contributor Author

kastov commented Feb 24, 2026

不如改 routing 更通用

{
  "protocol": [
    "bittorrent"
  ],
  "outboundTag": "block",
  "webhook": {
    "url": "https://example.com",
    "deduplication": 10
  }
}

Or with separate middleware for maybe future use with other functions

{
  "protocol": [
    "bittorrent"
  ],
  "outboundTag": "block",
  "middleware": {
    "webhook": {
      "url": "https://example.com",
      "deduplication": 10
    }
  }
}

If we select active method, i can update PR later today

@kastov
Copy link
Contributor Author

kastov commented Feb 24, 2026

现在不是还没有 API 能查到连接级信息吗

Method exist in gRPC API, but it disabled for at least 5 years.

SubscribeRoutingStats – it can be used to subscribe to all connections/logs/routing events. With some modifications I have checked it and seems like it contains all data which currently sent to access.log. Anyway, without proper filtration from Xray Core side it can easily DDoS the subscriber.

@RPRX
Copy link
Member

RPRX commented Feb 24, 2026

原来有这个啊我一直以为没有

不纠结了,给 routing 加个 webhook 吧(不需要 middleware),作为赞助方当然还是可以有些特殊需求的 feature requests

我觉得加给路由应该够通用了,入站出站用户信息都有

@Fangliding
Copy link
Member

我觉得attach 在black hole上可能好一点

@RPRX
Copy link
Member

RPRX commented Feb 24, 2026

改 blackhole 的话不就只有这个出站能用吗

@Fangliding
Copy link
Member

哎 机场

@kastov
Copy link
Contributor Author

kastov commented Feb 24, 2026

改 blackhole 的话不就只有这个出站能用吗

On one hand, considering that blackhole already has some settings, I think there's no problem adding a webhook object alongside them. However, as in the original concept, in this case we would only be able to send a webhook when the user is guaranteed not to have gained access to the requested resource (since the traffic was blocked). It's possible that this option won't suit everyone.

It's quite likely that some people would want to receive a webhook regardless of whether access to the site was blocked or not, and in that case placing the webhook inside a rule is perfectly justified.

If the goal is to provide the user (administrator) with functionality without limiting how they can use it, then I think option №2 (routing.rules.[N].webhook) would be the more correct approach from an implementation standpoint.

@RPRX
Copy link
Member

RPRX commented Feb 24, 2026

@kastov 请检查请求是否是异步的以免阻塞路由,另外是否有复用 TCP 连接、是否支持 unix domain socket

@kastov kastov changed the title feature: webhook outbound feature: webhook property in rules Feb 24, 2026
@kastov
Copy link
Contributor Author

kastov commented Feb 24, 2026

@kastov 请检查请求是否是异步的以免阻塞路由,另外是否有复用 TCP 连接、是否支持 unix domain socket

yeah, it's async — the HTTP POST fires in a separate goroutine, so it doesn't block the route. TCP connections are reused (keep-alives are on by default, response body is fully drained and closed). Unix domain sockets are supported too via the unix:// scheme.

i also updated documentation, please review XTLS/Xray-docs-next#823

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants