Edgeweir
使用指南

规则、IP 名单与 GeoIP

网站规则、全局规则、批量重定向、IP 名单,以及节点本地的 GeoIP 数据库。

概念

术语定义
规则一个阶段内的「表达式 + 动作」。网站规则只作用于一个网站;全局规则作用于所有集群的全部网站。
阶段请求处理过程中执行规则的固定位置,共 9 个。
表达式wirefilter 风格的类型化条件表达式,例如 ip.src in $blocked。
值表达式按请求算出字符串的表达式,作为重定向目标、改写路径、请求头与响应头的值或「设置查询参数」的值,例如 concat("/new", http.request.uri.path)。
批量重定向网站的精确匹配重定向表,每个网站最多 5000 条。
IP 名单命名的 IP 地址与 CIDR 集合,表达式用 $名称 引用;放行名单与拦截名单另外直接作用于所有网站。

控制台解析并检查表达式的字段、类型和动作后发布语法树;节点校验整份配置后把语法树编译执行,配置中不含可执行的 Lua 文本。

编辑网站规则

  1. 打开 网站,选择网站,进入「规则」页签。

  2. 在目标阶段右侧点击「添加规则」。

  3. 填写规则名称和「表达式」。「插入条件」追加常用条件(路径前缀、Host 等于、文件扩展名、IP 段、IP 名单、国家/地区、ASN、User-Agent 包含、请求方法、Cookie 等于、查询参数等于、User-Agent 通配、Referer 通配)并选中其中的示例值,直接输入即可替换;「插入字段」追加字段,选择「Cookie」「查询参数」「请求头」时先在出现的输入框中填写名称,按回车或点击「插入」;二者都以 and 连接。表达式有误时,编辑框下方显示「第 N 个字符:原因」,例如「第 11 个字符:大小比较只用于数字字段」;保存时被控制台拒绝的表达式同样显示位置和原因。

  4. 选择「动作」并填写动作参数,打开「启用」。新规则默认停用(经 API 保存时省略 enabled 也是停用),表达式默认是 true(匹配全部请求),保存前确认条件和动作。

  5. 拖动规则左侧的手柄调整同一阶段内的顺序。

  6. 点击「保存」。控制台提示「已保存」,并发布新的配置版本(原因「已更新规则与 IP 名单」)。

  7. 验证:例如表达式 http.request.uri.path eq "/blocked" 配合「拦截」动作:

    curl -sI -H 'Host: www.example.com' http://<节点 IP>/blocked

    返回 403,响应头含 X-Edgeweir-Error: policy-denied。

全局规则在 全局规则 页编辑,界面与网站规则相同(「源站覆盖」没有「源站组」),作用于所有网站,保存后向所有集群发布新版本。API:GET、PUT /api/v1/platform-rules。

停用的规则不下发到节点。

阶段与动作

阶段按下表顺序执行。

阶段可用动作作用
请求变换改写路径、请求头改写回源路径与查询参数;设置或移除请求头
重定向重定向返回 301、302、303、307 或 308;规则之后查批量重定向
配置覆盖设置按请求覆盖网站设置,见覆盖设置
自定义 WAF拦截、记录、放行、挑战拦截返回 403 或 451;记录只写日志;放行跳过同一作用域剩余的自定义 WAF 规则;挑战要求访客先通过挑战
限速限速固定窗口计数,超额返回 429 或 403
缓存覆盖设置只覆盖绕过缓存、强制 HTTPS 与 Gzip
回源请求头、源站覆盖设置或移除发往源站的请求头;选择源站组,覆盖回源 Host、SNI 与端口
响应变换响应头根据状态码和响应头设置、追加或移除响应头
压缩压缩算法限定本次响应可用的压缩算法及其优先顺序

动作字段

动作字段取值默认值
拦截状态码403 / 451403
挑战挑战类型Cookie 跳转 / JS 计算 / 工作量证明 / 图片验证码JS 计算
重定向目标「静态」:站内绝对路径(以 / 开头、不以 // 开头),或不含账号和空白的 http(s) URL;不含反斜杠与控制字符,最长 4096 字符;「表达式」:值表达式,见动态目标与查询参数静态,/
重定向状态码301 / 302 / 303 / 307 / 308301
重定向保留查询参数开 / 关关
改写路径目标「静态」:以 / 开头,不以 // 开头,不含 ?、#、\;「表达式」:值表达式静态,/
改写路径保留查询参数开 / 关开
重定向 / 改写路径设置查询参数最多 16 组「参数名」与「参数值」,「添加参数」增加一组;参数名为 1–64 个字母、数字或 . _ ~ -,不重复;参数值「静态」为可打印 ASCII,最长 256 字符,「表达式」为值表达式,见报头值与查询参数值无
重定向 / 改写路径移除查询参数参数名,逗号分隔,最多 16 个;不能同时出现在「设置查询参数」中空
请求头 / 响应头头名称1–64 个 token 字符,不能是受保护头x-custom
请求头 / 响应头值「静态」:最长 4096 字符,不含控制字符;「表达式」:值表达式,见报头值与查询参数值静态,空
请求头 / 响应头移除请求头或响应头开 / 关;开启时没有值关
响应头追加开 / 关;开启时在响应已有的同名头之外再加一行(例如 Link、Vary),关闭时替换关
覆盖设置各项设置见覆盖设置绕过缓存「开启」,其余「不更改」
源站覆盖源站组「默认组」或网站的源站组,见源站组网站的第一个源站组;没有时为「默认组」
源站覆盖回源 Host主机名或 IP,可带端口,写法同源站的回源 Host;空为不更改空
源站覆盖SNI主机名;空为不更改空
源站覆盖端口1–65535;空为不更改空
压缩算法优先顺序Zstandard、Brotli、Gzip 中的若干个:「添加算法」追加,上下移动调整顺序;列表为空时显示「不压缩」不压缩
限速预设宽松(每分钟 300 次)、标准(每分钟 100 次)、严格(每分钟 20 次)或自定义;选择自定义后显示下面两项标准
限速每个窗口请求数1–100000100
限速窗口(秒)1–360060
限速限速键ip.src、http.host、tls.ja4 或 http.request.headers.<名称>(选择「请求头」后填写名称)ip.src
限速状态码429 / 403429

受保护头不能通过规则设置或移除:Host、Authorization、Proxy-Authorization、Cookie、Set-Cookie、Content-Length、Transfer-Encoding、Connection、Upgrade、TE、Trailer、CDN-Loop,以及以 X-Edgeweir- 开头的头。

请求头和响应头的静态值与值表达式都写入配置版本并下发到节点,不要填入 API 密钥或其他秘密。

「源站覆盖」至少修改一项:选择「默认组」以外的源站组,或填写「回源 Host」「SNI」「端口」之一。

覆盖设置

「覆盖设置」的每一项默认「不更改」,数值项留空即不更改。

字段取值阶段作用
绕过缓存不更改 / 开启 / 关闭配置、缓存本请求绕过或使用缓存
强制 HTTPS不更改 / 开启 / 关闭配置、缓存HTTP 请求重定向到 HTTPS
Gzip不更改 / 开启 / 关闭配置、缓存「关闭」:本次响应不用 gzip;「开启」:重新允许被先前规则关闭的 gzip
Brotli、Zstandard不更改 / 开启 / 关闭配置同 Gzip
WebSocket不更改 / 开启 / 关闭配置覆盖网站的「WebSocket」
Under Attack不更改 / 开启 / 关闭配置覆盖网站的 Under Attack;全局 Under Attack 不受影响
CC 防护不更改 / 开启 / 关闭配置「关闭」:本请求不受 CC 级别挑战与单 IP 自动封禁,计数照常
CC 最高级别不更改 / Cookie 跳转 / JS 计算 / 工作量证明 / 图片验证码配置本请求的 CC 级别不高于所选级别
回源连接超时(秒)0.1–120配置覆盖源站池的连接超时
回源发送超时(秒)、回源读取超时(秒)0.1–3600配置覆盖源站池的发送、读取超时
日志采样率(%)0–100配置本请求的访问日志采样率
请求体上限(MiB)0–10240,0 不限配置覆盖网站的请求体上限;需要节点能力 site-content-v1

后命中的规则逐项覆盖先命中的规则。压缩算法的开关只在网站已开启的算法中生效,规则不能开启网站未开启的算法。

动态目标与查询参数

  1. 在重定向或改写路径规则的「目标」旁选择「表达式」。

  2. 填写值表达式。编辑框下方显示「第 N 个字符:原因」时按函数与字段修改。

  3. 按需切换「保留查询参数」,填写「设置查询参数」与「移除查询参数」。

  4. 点击「保存」。

  5. 验证:例如重定向规则的表达式为 starts_with(http.request.uri.path, "/old/"),目标为 regex_replace(http.request.uri.path, "^/old/", "/new/"),打开「保留查询参数」,「移除查询参数」为 utm_source:

    curl -sI -H 'Host: www.example.com' 'http://<节点 IP>/old/a?utm_source=x&id=1'

    返回 301,Location: /new/a?id=1。

值表达式是字符串字面量、字符串字段或返回字符串的函数调用:

concat("https://www.example.com", http.request.uri.path)
regex_replace(http.request.uri.path, "^/old/(.*)$", "/new/${1}")
wildcard_replace(http.request.full_uri, "https://*.example.com/*", "https://example.com/${1}/${2}")
项目行为
重定向结果必须是以单个 / 开头的路径,或带主机、不含账号的 http(s) URL;不含空白、控制字符和反斜杠
改写结果必须以单个 / 开头,不含 ?、#、\ 与控制字符
结果无效请求返回 503(X-Edgeweir-Error: policy-unavailable)
默认查询串重定向不带原请求的查询串;改写路径保留查询串
处理顺序重定向打开「保留查询参数」时先把原请求的查询串接到目标后(目标已有查询串时用 & 连接);改写路径关闭「保留查询参数」时先清空查询串。然后从整个查询串中删除「移除查询参数」与「设置查询参数」中的同名参数,再按参数名顺序追加「设置查询参数」
参数名比较每个参数第一个 = 之前的部分,区分大小写
参数值字母、数字与 - . _ ~ 以外的字符一律百分号编码
片段与空查询目标中 # 之后的片段保留在最后;没有参数时不留 ?

报头值与查询参数值

  1. 在请求头或响应头规则的「值」旁、或「设置查询参数」某一行的参数名后选择「表达式」。

  2. 填写值表达式,例如回源阶段的请求头 X-Client-Country 取 ip.geoip.country,X-Req 取 http.request.id。编辑框下方显示「第 N 个字符:原因」时按函数与字段修改。

  3. 响应头需要在已有同名头之外再加一行时打开「追加」。

  4. 点击「保存」。规则修改热更新,不重载 nginx。

  5. 验证:例如回源阶段的请求头规则 X-Req = http.request.id,源站收到的 X-Req 与响应头 X-Request-Id 相同;响应变换阶段两条追加 Link 的规则使响应带两行 Link:

    curl -sI -H 'Host: www.example.com' 'http://<节点 IP>/' | grep -i '^link:'
项目行为
可用阶段请求头:请求变换、回源;响应头:响应变换(可读取响应字段);查询参数:重定向、改写路径所在的阶段
报头值结果最长 4096 字节、不含控制字符(\x00–\x1f、\x7f);结果无效或求值失败(正则超出执行预算、函数结果超过 8192 字节)时跳过这一条报头动作,请求照常继续,不返回 503
跳过时的日志每条规则在每个节点每 60 秒最多写一条 NOTICE 级 nginx 错误日志 edgeweir: header value skipped site=<网站 ID> rule=<规则 ID>,内容不含报头名与值;nginx 为请求期间的日志附加客户端 IP、请求行和 Host
追加在已有同名头之外再加一行,不合并、不去重;之后的规则读取 http.response.headers["名称"] 得到以 , 连接的全部行;与「移除」互斥
查询参数值结果照旧百分号编码(字母、数字与 - . _ ~ 以外的字节);求值失败时与动态目标相同,请求返回 503(X-Edgeweir-Error: policy-unavailable)
求值时机请求阶段的值按当时的请求求值(改写之后的路径、查询与 http.request.uri.args);响应头的值在边缘层的响应头过滤阶段求值,缓存命中与回源响应都会重新计算
节点要求rules-v3,见节点能力与发布

执行顺序

项目行为
放行与拦截名单最先执行。地址命中拦截名单返回 403;命中放行名单只豁免拦截名单,不跳过规则;同时在两种名单中时放行优先
作用域每个阶段先执行全局规则,再执行网站规则;同一作用域内按列表顺序
结束请求拦截、重定向(含批量重定向)和限速超额结束请求
批量重定向重定向阶段的全局规则与网站规则之后查表
放行只跳过同一作用域剩余的自定义 WAF 规则,不跳过另一作用域或限速;网站的放行规则不能绕过全局规则中的拦截
叠加其余动作依次叠加,后执行的覆盖先执行的设置;覆盖设置与源站覆盖逐项覆盖,后命中的压缩规则替换先命中的
排序拖动只改变同一阶段内的顺序

动作行为

动作行为
改写路径改变发往源站的路径与查询串;后续阶段的表达式读取改写后的路径、查询串、查询参数与扩展名(http.request.full_uri 不变);缓存规则、缓存键、刷新与批量重定向仍按改写前的请求
响应变换同时作用于缓存命中和回源响应
覆盖设置:Gzip、Brotli、Zstandard只影响本次响应,不绕过缓存。网站在边缘压缩(任一算法开启)时,缓存中仍是同一份未压缩对象;网站不在边缘压缩时,「Gzip」关闭移除发往源站的 Accept-Encoding,缓存按源站的 Vary 区分变体,源站不带 Vary: Accept-Encoding 时可能命中此前缓存的压缩对象
覆盖设置:强制 HTTPS网站没有证书时请求返回 503
覆盖设置:日志采样率按该比例记录匹配的请求;网站的访问日志采样率为「关闭」时这些日志同样保存,见访问日志
源站覆盖选中组的源站之间照常负载均衡、重试、健康检查与会话保持;「端口」作用于组内每个源站;「回源 Host」不影响 S3 源站;缓存键不含源站组,见源站组
压缩算法只在列表内、网站已开启且未被覆盖设置关闭的算法中,按请求 Accept-Encoding 的 q 值协商,q 值相同时按列表顺序;「不压缩」时不压缩;不绕过缓存
拦截、限速超额响应头 X-Edgeweir-Error: policy-denied;限速超额另带 Retry-After(窗口秒数)
挑战请求带有级别足够的通行凭证时继续执行后续规则,否则返回挑战页(非 GET/HEAD 请求返回 403 与 X-Edgeweir-Challenge: required);放行规则跳过 Under Attack 与 CC 挑战,但在放行之前命中的挑战规则仍然生效。见挑战与 CC 防护
记录不改变响应。节点按规则与分钟统计命中的请求数(不含节点自己的预热请求),网站「安全」页签的「记录命中」卡片按时间范围列出命中最多的规则(全局规则带「(全局)」,已删除的规则显示为「已删除的规则」),数值为近似值:每个节点每分钟最多上报 20 条规则。所在集群有节点不支持(缺少能力 rule-log-v1)时卡片提示「所在集群有节点不上报记录命中」。此外每条规则在每个节点每 60 秒最多写一条 NOTICE 级 nginx 错误日志,内容为网站 ID 和规则 ID;nginx 为请求期间的日志附加客户端 IP、请求行和 Host

限速

项目行为
范围单节点固定窗口,不是全网共享配额;每个网站独立计数,全局规则中的限速也按网站分别计数
键作用域、规则 ID 与键值合成 MD5 摘要计数
内存每个已发布网站固定 256 KiB 共享内存(节点参数 --rate-limit-dict-kb),约容纳 1980 个计数,网站之间不借用;每个集群最多 512 个已发布网站(默认合计 128 MiB);记录去重另用 1 MiB
内存不足分区满时新客户端的请求不计数、直接放行,已有计数的客户端照常限速;节点每个网站每分钟最多写一条 WARN 级 nginx 错误日志,含累计放行次数。节点缺少网站的分区时返回 503(X-Edgeweir-Error: rate-limit-unavailable)
重载与重启nginx 重载保留计数;节点重启、或网站被移除后重新加入时清零
热更新同一批网站的规则和名单修改不重载;新增或移除网站需要重载,既有分区的名称与大小不变

表达式

ip.src in $blocked
http.request.uri.path matches "^/(admin|private)/" and not ssl eq true
http.request.method in {"POST" "PUT"}
http.request.headers["x-region"] eq "nz"
ip.geoip.country eq "NZ" and ip.geoip.asnum in {64512 64513}
http.response.code ge 500
lower(http.host) eq "www.example.com"
len(http.request.uri.query) gt 1024
url_decode(http.request.uri.query) contains "<script"
not starts_with(http.request.uri.path, "/api/")
http.request.uri.path.extension in {"jpg" "png" "webp"}
http.request.cookies["role"] eq "admin"
http.user_agent wildcard "*curl*"
http.referer strict wildcard "https://*.example.com/*"
substring(sha256(http.request.uri.path), 0, 8) eq "a1b2c3d4"

字段

字段类型值
http.host字符串请求的 Host
http.request.method字符串请求方法
http.request.uri.path字符串nginx 规范化后的路径
http.request.uri.path.extension字符串路径最后一段中最后一个 . 之后的部分,小写;没有时为空字符串
http.request.uri.query字符串查询字符串,不含 ?
http.request.uri字符串原始请求 URI(路径加查询)
http.request.full_uri字符串scheme:// 加 Host(小写、不含端口)再加原始请求 URI;不随改写变化
http.request.headers["name"]字符串名称不区分大小写;多个值用 , 连接
http.request.cookies["名称"]字符串请求 Cookie 头中第一个同名 Cookie 的原始值(不解码,引号保留);名称区分大小写,为 1–64 个 token 字符;没有时为空字符串。Cookie 以 ; 分隔,每一对及其名称和值两侧的空格与制表符忽略,没有 = 的一段跳过;多个 Cookie 头以 ; 连接
http.request.uri.args["名称"]字符串查询串中第一个同名参数的原始值(名称与值都不解码,配合 url_decode);名称为第一个 = 之前的部分,区分大小写,为 1–64 个不含 " # & = 的可打印 ASCII 字符;没有 = 的参数值为空字符串;没有该参数时为空字符串;随改写变化
http.referer、http.user_agent字符串等同 http.request.headers["referer"]、["user-agent"],随请求头规则变化
http.request.version字符串HTTP/1.0、HTTP/1.1、HTTP/2.0 或 HTTP/3.0
http.request.scheme字符串http 或 https
http.request.id字符串节点的请求 ID,与响应头 X-Request-Id 相同
http.request.timestamp.sec整数节点收到请求时的 Unix 秒
edge.server_port整数接收请求的监听端口
http.response.code整数响应状态码;仅响应变换与压缩阶段
http.response.headers["name"]字符串响应头;仅响应变换与压缩阶段
http.response.content_type.media_type字符串响应 Content-Type 去掉参数后的小写媒体类型;仅响应变换与压缩阶段
http.response.cache_status字符串边缘缓存的状态:HIT、MISS、BYPASS、EXPIRED、STALE、UPDATING 或 REVALIDATED(同 X-Cache);节点自身生成的响应(拦截、重定向、错误页)以及不经过缓存的 WebSocket 与 gRPC 为空字符串;仅响应变换与压缩阶段
ip.srcIP访客地址,按集群的访客 IP 设置取得:直连时为 TCP 客户端地址;PROXY protocol 时为 PROXY 头中的地址(HTTP/3 为 UDP 对端);可信代理报头时为可信代理在报头中写明的地址
ip.peerIP直连对端:TCP 客户端地址(HTTP/3 为 UDP 对端),不受访客 IP 设置影响;直连模式下等于 ip.src。需要 client-ip-v1
ssl布尔HTTPS 请求为 true
ip.geoip.country字符串ISO 国家代码;无记录时为空字符串
ip.geoip.subdivision字符串City MMDB 中的一级行政区代码,没有代码时为其英文名称;无记录或 City MMDB 的国家与 ip.geoip.country 不一致时为空字符串
ip.geoip.asnum整数ASN;无记录时为 0
ip.geoip.as_name字符串AS 名称:来自与 ASN 相同的数据库,IPinfo Lite 的 as_name 或 ASN MMDB 的 autonomous_system_organization;无记录时为空字符串
tls.ja4字符串连接的 JA4 TLS 客户端指纹;明文 HTTP 为空字符串,见 JA4

运算符与字面量

语法适用类型说明
eq、ne全部IP 类型按地址或 CIDR 包含关系比较
lt、le、gt、ge整数有序比较
contains字符串子串匹配
matches字符串正则匹配
wildcard "模式"字符串整串通配匹配:* 匹配零个或多个字节(至多 8 个),\*、\\ 表示字面量,其他反斜杠无效;ASCII 字母不区分大小写;模式最长 1024 字节,不含控制字符
strict wildcard "模式"字符串同 wildcard,区分大小写
in {…}全部集合,元素以空白分隔
in $名称IP引用 IP 名单;只用于字段,不用于函数结果
not、and、or、( )—优先级 not → and → or
字面量—字符串用双引号(JSON 转义);整数;true / false;IP 与 CIDR 不加引号;单独的 true 是有效表达式
函数(参数, …)—比较的左侧可以是函数,按返回类型适用上述运算符;返回布尔的函数可单独作为条件

函数

所有字符串按 UTF-8 字节处理。参数是字段、字符串字面量或另一个函数调用。

函数返回说明
lower(s)、upper(s)字符串只转换 ASCII 字母
len(s)整数字节数
starts_with(s, 前缀)、ends_with(s, 后缀)布尔按字节比较前缀、后缀;空串总是匹配
url_decode(s)字符串解码一遍:%XX(十六进制不区分大小写)解为字节,+ 解为空格;不完整或非十六进制的 % 原样保留
concat(s1, s2, …)字符串2–8 个参数按顺序拼接
regex_replace(s, "正则", "替换串")字符串替换第一个匹配;正则见正则;没有匹配时返回原串
wildcard_replace(s, "通配模式", "替换串"[, "s"])字符串通配模式匹配整个字符串:* 匹配零个或多个字节(至多 8 个),\*、\\ 表示字面量;默认 ASCII 不区分大小写,第四个参数 "s" 区分;前面的 * 取最短匹配;不匹配时返回原串
url_encode(s)字符串RFC 3986 未保留字符(字母、数字与 - . _ ~)以外的字节一律编码为 %XX(大写十六进制)
base64_encode(s)字符串标准字母表,带填充
base64_decode(s)字符串接受标准与 URL 安全字母表(可混用)、有无填充,忽略末尾多余的位;含其他字符、空白、位置不对或多余的填充、长度除以 4 余 1 时返回空字符串
md5(s)、sha1(s)、sha256(s)字符串摘要的小写十六进制
substring(s, 起点[, 长度])字符串按字节截取:起点从 0 开始,负数从末尾数(超出开头时从第 1 个字节起);省略长度时到末尾;起点不小于长度时为空字符串。起点为 -65536–65536、长度为 0–65536 的整数字面量
to_string(x)字符串整数写成十进制,布尔为 true / false,IP 为节点看到的地址文本,字符串原样返回;参数可以是任何类型的字段或函数
项目规则
使用位置条件(含缓存规则条件)可用 regex_replace、wildcard_replace 以外的函数;值表达式可用全部函数,regex_replace、wildcard_replace 每个表达式各至多一次
参数类型除 to_string(任何类型)与 substring 的起点、长度(整数字面量)外,参数都是字符串
字面量参数正则、通配模式、替换串与 "s" 必须是字符串字面量;通配模式与替换串最长 1024 字节,不含控制字符
替换串${1}–${8} 引用正则的捕获组或通配模式的 *,编号不超过组数或 * 数;未参与匹配的组替换为空串;其他 $ 是字面量;捕获保留原串的大小写
嵌套至多 4 层
结果长度函数算出的字符串超过 8192 字节时求值失败
求值失败正则超出执行预算、结果超长或动态目标无效时,请求返回 503(X-Edgeweir-Error: policy-unavailable)

IP 语义

项目行为
IPv4 映射地址::ffff:a.b.c.d 与对应 IPv4 地址相同;前缀短于 96 位的映射 CIDR 视为歧义被拒绝
IPv4 兼容写法::a.b.c.d 是普通 IPv6 地址(::1.2.3.4 保存为 ::102:304/128),不与 IPv4 等同
NAT64独立的 IPv6 地址,不与 IPv4 等同
CIDR保存前清除主机位
拒绝带前导零的写法(八进制歧义)、zone ID(%)

正则

matches 与 regex_replace 使用同一个子集。控制台校验、节点校验与节点执行(PCRE)接受同一个子集;各引擎语义不一致的构造一律拒绝。

项目规则
匹配对象值的 UTF-8 字节,区分大小写;.、[^…]、\D、\W 每次匹配一个字节(一个汉字为 3 个字节)
长度与字符最多 256 个可打印 ASCII 字符;其他字符写作 \t \n \r \f 或 \x00–\x7f
锚点^ 值的开头;$ 值的末尾;\b \B ASCII 单词边界
字符. 为除 \n 外的任意字节;\d \D \w \W 按 ASCII;反斜杠加 ASCII 标点表示该标点,字面 ] { } 须转义
字符类[…] [^…] 可含字符、\d \D \w \W 与范围 a-z;- 只在首尾为字面量,其余位置写 \-;类内 [ 须转义
量词* + ? {n} {n,} {n,m}(n ≤ m ≤ 1000,无前导零),可加 ? 变为惰性;只跟在字符、字符类或转义之后
分组( ) 与 |;分组不带量词
不支持\s \S \v(写作 [ \t\r\n\f] 等显式类)、大于 \x7f 的 \xHH、\z \A \Q \p{…} \K 等其他转义、反向引用与八进制、(? 开头的分组、占有与叠加量词、{,n}、[[:alpha:]]、空字符类与 []…]
执行预算节点使用 PCRE,匹配上限 10000、深度上限 100;执行出错时请求返回 503(X-Edgeweir-Error: policy-unavailable)

已保存的规则或缓存规则条件使用了不再支持的构造(例如 \s)时,保存所属网站或它的规则返回 RULE_INVALID,改写后再保存。其他发布(别的网站、ACME 挑战、系统设置)不受影响:该规则沿用上次编译的结果,并发出告警「已存规则不再通过校验,沿用上次编译结果」;从未编译过的这类规则会让所属网站暂不下发。已停用的网站不编译规则。

支持范围是 wirefilter 风格的子集,不是 wirefilter 的完整实现。

复杂度上限

项目上限
表达式长度4096 字符;缓存规则条件 16384 字符
token 数512
语法嵌套16 层
基本条件、函数与参数合计 128 个
集合元素256 个
函数嵌套4 层
函数结果8192 字节
规则数每个网站 64 条;全局规则 32 条
批量重定向每个网站 5000 条

批量重定向

网站的精确匹配重定向表:每条把一个来源重定向到一个静态目标。按前缀或通配重定向时,使用重定向规则与 wildcard_replace。

  1. 打开 网站,选择网站,进入「批量重定向」页签。

  2. 点击「添加重定向」,填写「来源」「目标」,选择「状态码」,按需打开「保留查询参数」。

  3. 或点击「导入」,在「导入重定向」中每行填写一条(「每行一条:来源 目标 [状态码]」),按需打开「替换现有条目」,点击「导入」。

  4. 点击「保存」。控制台提示「已保存」,并发布新的配置版本(原因「已更新规则与 IP 名单」)。

  5. 验证:

    curl -sI -H 'Host: www.example.com' http://<节点 IP>/old

    返回该条目的状态码,Location 为目标。

字段取值默认值
来源/路径(匹配网站的全部域名)或 域名/路径(只匹配该域名);2–512 字节,不含空白、? 与控制字符;域名小写,须是网站的域名或网站泛域名下一级的子域名/
目标同静态重定向目标,最长 1024 字节/
状态码301 / 302 / 307 / 308301
保留查询参数开 / 关;开启时把原请求的查询串接到目标后(目标已有查询串时用 & 连接)关
项目行为
匹配按客户端原始请求(改写之前)的 Host(小写、不含端口)与规范化后的路径精确匹配,查询串不参与;先查 域名/路径,再查 /路径
顺序在重定向阶段的全局规则与网站规则之后;命中重定向规则的请求不再查表
导入每行的字段以空白分隔(行内没有空白时以逗号分隔),状态码默认 301;跳过空行与以 # 开头的行;与已有条目来源相同时替换该条目;打开「替换现有条目」时替换整张表;有无效行时提示「第 N 行无效」,不导入任何一行
列表每页 50 条;「筛选」按来源或目标查找
更新节点热更新,不重载 nginx
审计修改写审计 site.bulk_redirects_update(记录条数)
上限每个网站 5000 条,来源不重复
节点要求rules-v2,见节点能力与发布

IP 名单

  1. 打开 IP 名单,点击「创建名单」。
  2. 填写「名称」:以字母或下划线开头,只含字母、数字和下划线,1–64 字符。
  3. 选择「动作」:「供规则引用」「拦截」或「放行」。
  4. 在「IP 地址和 CIDR」中填写条目,以换行、空格或逗号分隔。
  5. 点击「保存」。
  6. 验证:列表显示 $名称 和「N 条记录」,拦截与放行名单另带「拦截」「放行」标记;在规则或缓存规则条件中用 ip.src in $名称 引用。
动作作用
供规则引用只作为集合,由规则引用
拦截拦截名单:作用于所有集群的全部网站,无需规则;地址命中时返回 403,先于所有规则执行
放行放行名单:作用于所有集群的全部网站;地址命中时豁免拦截名单与封禁,不跳过规则
项目行为
名称所有名单共用一个命名空间,名称唯一;创建后不可修改;保存规则时按名称绑定到名单 ID
引用任何网站规则、全局规则或缓存规则条件都可引用任何名单,含拦截与放行名单;L4 应用 的「放行名单」「拦截名单」同样可以选择任何名单
L4 应用「拦截」「放行」动作只作用于网站;L4 应用只按自己选择的名单判断
修改条目和「动作」可随时修改;创建、修改或删除名单都向所有集群发布新版本(原因「已更新规则与 IP 名单」),节点热更新
删除被规则、缓存规则条件或 L4 应用引用的名单不能删除(「IP 名单正在被使用:…」,列出前 5 个引用者:规则名,网站规则带网站名;缓存规则所在的网站名;L4 应用名)
条目IPv4 / IPv6 地址或 CIDR;清除主机位、去重、排序;拒绝前导零写法和 zone ID
配额最多 128 份名单、合计 50000 条记录;每份最多 10000 条;不增加条目数的修改总能保存
回滚网站配置回滚沿用当前的名单和全局规则;引用已删除名单时拒绝回滚

API:GET、POST /api/v1/ip-lists,PUT、DELETE /api/v1/ip-lists/{id}。

节点能力与发布

项目行为
能力规则和放行/拦截名单需要节点能力 rules-v1;ip.geoip.country、ip.geoip.subdivision 需要 geoip-city-v1;ip.geoip.asnum 需要 geoip-asn-v1;挑战动作需要 challenge-v1;tls.ja4(字段或限速键)需要 ja4-v1;使用 ip.geoip.subdivision 时控制台另外检查 geoip-subdivision-v1(不写入配置)
规则扩展以下任何一项需要 rules-v2:函数与 http.request.full_uri、http.request.uri.path.extension、http.response.content_type.media_type;表达式目标、查询参数编辑,以及重定向打开或改写路径关闭「保留查询参数」;源站覆盖;压缩阶段;只在配置阶段可用的覆盖项与 Gzip「开启」;不是构建器形状的缓存规则条件与「浏览器 TTL(秒)」;批量重定向;默认组以外的源站组
表达式变量与报头值以下任何一项需要 rules-v3:http.request.cookies[…]、http.request.uri.args[…]、http.referer、http.user_agent、http.request.version、http.request.scheme、http.request.id、http.request.timestamp.sec、edge.server_port、ip.geoip.as_name(另需 geoip-asn-v1)、http.response.cache_status;url_encode、base64_encode、base64_decode、md5、sha1、sha256、substring、to_string;wildcard、strict wildcard;请求头、响应头与查询参数的表达式值;响应头「追加」;重定向 303;错误页的 {{time}}、{{path}}
直连对端读取 ip.peer 的配置需要 client-ip-v1
已有配置没有用到规则扩展的配置与之前相同,不要求 rules-v2;构建器形状的缓存规则仍以原来的结构化条件下发;没有用到 rules-v3 各项的配置同样逐字节不变
控制台与 AccessKey保存时即使集群内有活动节点缺少所需能力也照常发布;缺少能力的节点保留 last-known-good 配置,集群与节点 中显示「需要升级」,见节点升级
服务账号与后台任务它们发布的配置引入新能力时,检查集群内所有活动节点(含暂时离线的节点);有节点缺少能力时拒绝(NODE_CAPABILITY_REQUIRED,「节点尚不支持:…(<节点>)」),原配置与版本不变
界面集群有活动节点缺少 rules-v2 时,「规则」「缓存」「批量重定向」页签显示「所在集群有节点不支持规则扩展,暂时无法使用」,不能再选用规则扩展,已设置的项仍可修改或清除;「批量重定向」只读。缺少 rules-v3 时「规则」页签显示「所在集群有节点不支持,暂时无法开启」:菜单里不列 rules-v3 的字段与条件,「表达式」值、「追加」与 303 不能新选,已设置的仍可修改或清除;「错误页」页签不列 {{time}}、{{path}},模板用到它们时显示同样的提示
未知能力节点拒绝含未知能力或未知枚举的配置,继续使用 last-known-good

配置 GeoIP 数据库

GeoIP 字段读取节点本地的 MMDB 文件。节点不自动下载更新,也不向数据商发送访客地址。

数据库字段获取方式许可证与更新署名
IPinfo Lite国家、ASN节点发布镜像内置;安装包和压缩包节点从 IPinfo 下载(免费账号)CC BY-SA 4.0;IPinfo 每日更新,镜像内为构建当天的快照IP address data is powered by IPinfo
City MMDB,如 DB-IP Lite City国家、一级行政区运营者下载DB-IP Lite:CC BY 4.0,按月更新IP Geolocation by DB-IP
ASN MMDB,如 DB-IP Lite ASNASN运营者下载同上同上
项目行为
优先级国家和 ASN 优先取 IPinfo Lite,无记录时取 City / ASN MMDB
一级行政区只来自 City MMDB;City MMDB 的国家与最终国家一致时才有值
镜像内置数据/usr/share/edgeweir-node/geoip/ipinfo_lite.mmdb,构建时按 IPinfo 公布的 sha256 校验;同目录 NOTICE 记录下载时间和 sha256
控制台署名防护设置 → GeoIP 数据库 带 IPinfo 署名链接
  1. 容器节点使用发布镜像时,国家和 ASN 无需配置;更新数据时拉取新镜像,或挂载另行下载的副本并设置 EDGEWEIR_GEOIP_IPINFO。

  2. 安装包或压缩包节点:从 IPinfo 下载 ipinfo_lite.mmdb。需要按一级行政区匹配时,另外下载 City MMDB。核对来源、许可和完整性,记录下载日期。

  3. 把文件放在节点可读的只读目录,为节点设置环境变量:

    /etc/default/edgeweir-node
    EDGEWEIR_GEOIP_IPINFO=/etc/edgeweir-node/geoip/ipinfo_lite.mmdb
    EDGEWEIR_GEOIP_CITY=/etc/edgeweir-node/geoip/dbip-city-lite.mmdb

    容器部署挂载该目录并以 -e 传入同样的变量。

  4. 重启节点,装入数据库:

    sudo systemctl restart edgeweir-node
  5. 验证:防护设置 → GeoIP 数据库 中该节点显示「国家:可用」「ASN:可用」;配置了 City MMDB 时另显示「省份:可用」。

变量参数默认值说明
EDGEWEIR_GEOIP_IPINFO--geoip-ipinfoautoIPinfo Lite MMDB 路径;auto 使用镜像内置的数据库(存在时),off 关闭
EDGEWEIR_GEOIP_CITY--geoip-city空City MMDB 路径
EDGEWEIR_GEOIP_ASN--geoip-asn空ASN MMDB 路径
项目行为
能力上报有国家数据(IPinfo Lite 或 City MMDB)时上报 geoip-city-v1,有 City MMDB 时上报 geoip-subdivision-v1,有 ASN 数据时上报 geoip-asn-v1;当前版本节点另报 geoip-country-v1
旧版节点未上报 geoip-country-v1 的节点只在有 City MMDB 时上报 geoip-city-v1,控制台视其具备 geoip-subdivision-v1
缺少能力节点拒绝使用其缺少的 GeoIP 字段的配置,保留 last-known-good
文件无效指定的 MMDB 文件无效或数据库类型不符时节点 agent 不能启动;镜像内置的 IPinfo Lite 无效时 agent 记录错误并不使用它
查询agent 读取文件,经权限 0600 的本机 Unix socket 向 Lua worker 提供结果;每个 worker 缓存最多 10000 个结果、有效期 5 分钟;单次查询超时 200 毫秒
查询失败网站规则、全局规则(含值表达式)或缓存规则条件使用 GeoIP 字段时,该网站的每个请求都要查询;服务不可用时这些请求返回 503
更新先在一个节点替换文件或镜像并重启、验证,再更新其余节点;不要原地改写正在使用的 MMDB

限制

项目说明
限速只有单节点固定窗口;没有全网共享配额和滑动窗口
表达式wirefilter 风格子集;只有内置函数,不支持自定义函数和原始 Lua
字符串替换regex_replace、wildcard_replace 只用于值表达式(重定向目标、改写路径、报头值与查询参数值),每个表达式各一次;regex_replace 只替换第一个匹配
Cookie 与查询参数按名称取第一个值,不解码;不能列举全部 Cookie 或参数,名称不能用通配
批量重定向只做精确匹配,目标为静态值
受保护头见动作字段;不能通过规则修改
压缩覆盖设置与压缩规则只能在网站已开启的算法中选择
源站组缓存键不含源站组
GeoIP 数据镜像内置的 IPinfo Lite 是构建当天的快照;一级行政区需运营者提供 City MMDB;精度取决于所选数据库

故障排查

现象原因处理
编辑框下方显示「第 N 个字符:原因」该位置语法、字段、类型、函数或正则不受支持,原因说明具体问题按上方语法表修改
保存时提示「检查规则「名称」的字段」该规则的这个字段无效,例如目标格式、参数名重复或同时设置与移除同一参数;表达式无效时提示位置与原因按动作字段表修改
保存提示「规则无效」动作不属于该阶段、头名称受保护、重定向目标或改写路径格式无效;源站覆盖选择了网站没有的源站组按动作字段表修改;先在「源站」页签添加该组的源站
「网站没有这些域名:…」批量重定向来源中的域名不属于该网站改用网站的域名,或写成 /路径
「第 N 行无效」导入的该行字段数、来源、目标或状态码无效修正该行后重新导入
「N 条无效」批量重定向表中有无效或来源重复的条目修正标出的条目
「所在集群有节点不支持规则扩展,暂时无法使用」集群内有活动节点缺少 rules-v2升级节点,见节点升级
「IP 名单不存在:…」表达式引用的名单(列出的名称)不存在先在 IP 名单 创建该名单,或修正名称
「IP 名单名称已存在」已有同名名单使用其他名称
「IP 名单正在被使用:…」删除仍被列出的规则、网站的缓存规则条件或 L4 应用引用的名单先从这些规则与 L4 应用中移除引用
「IP 名单已达上限(128 份名单、50,000 条记录)」超过配额合并或删除名单
节点显示「需要升级」节点缺少配置所需能力(rules-v1、rules-v2、GeoIP 能力等),保留 last-known-good 配置升级节点或配置 GeoIP 数据库
「节点尚不支持:…(<节点>)」服务账号或后台任务发布的配置用到 rules-v1、rules-v2 或 GeoIP 能力,而集群内有活动节点缺少这些能力升级节点或配置 GeoIP 数据库
503,X-Edgeweir-Error: policy-unavailable正则执行超出预算、函数结果超过 8192 字节、动态目标、改写路径或查询参数值的表达式无效,或 GeoIP 查询失败简化正则或表达式;检查值表达式的结果;检查节点 GeoIP 服务
表达式值的请求头或响应头没有出现,节点日志有 header value skipped site=… rule=…该规则算出的值超过 4096 字节、含控制字符,或求值失败;节点跳过这一条报头动作用 substring、url_encode 等限制或清理值;同一条规则 60 秒内只记一条日志
「所在集群有节点不支持,暂时无法开启」(规则页签)集群内有活动节点缺少 rules-v3升级节点,见节点升级
开启强制 HTTPS 的规则使请求返回 503网站没有证书在「HTTPS」页签选择证书
限速在多个节点间未合并计数限速按单节点计数按节点数折算阈值
部分访客未被限速,节点日志有 rate limit partition full网站的限速分区已满,新客户端不计数调大节点参数 --rate-limit-dict-kb
在 GitHub 上编辑

本页目录