a6-plugin-response-rewrite
概览
response-rewrite 插件会在 Apache APISIX向客户端返回响应前重写响应属性。
该插件会将响应返回给客户端。你可以无条件或根据匹配条件修改 HTTP 状态码、响应头和响应体。它在 header_filter 和 body_filter 阶段运行,因此即使前置插件(例如认证插件)调用 ngx.exit,它也会执行。
适用场景
- 覆盖返回给客户端的 HTTP 状态码。
- 添加、设置或移除响应头(例如安全请求头、CORS)。
- 替换整个响应体(静态内容、错误消息)。
- 使用正则表达式过滤器修改响应体的部分内容。
- 有条件地应用响应修改(例如仅针对特定状态码)。
- 提供经过 Base64 解码的二进制内容(图像、protobuf)。
插件配置参考(路由/服务)
| 字段 | 类型 | 是否必填 | 默认值 | 说明 |
|---|---|---|---|---|
status_code | integer | 否 | — | 新的 HTTP 状态码(200–598)。如果未设置,则使用原始状态码。 |
body | string | 否 | — | 新的响应体。Content-Length 会自动重置。不能与 filters 同时使用。 |
body_base64 | boolean | 否 | false | 在发送前将 body 从 Base64 解码。仅解码插件配置的响应体,不解码上游 响应。 |
headers | object | 否 | — | 使用 set、add 和 remove 字段操作请求头。 |
headers.set | object | 否 | — | 设置(覆盖)响应头。值为键值对,支持 NGINX 变量。 |
headers.add | array[string] | 否 | — | 追加响应头。例如:["Name: value", ...]。即使请求头已存在也会添加。 |
headers.remove | array[string] | 否 | — | 移除响应头。要移除的请求头名称列表。 |
vars | array[array] | 否 | — | 使用 lua-resty-expr 语法进行条件匹配。仅当条件匹配时插件才会执行。 |
filters | array[object] | 否 | — | 用于修改响应体的正则过滤器。不能与 body 同时使用。 |
filters[].regex | string | 是 | — | 要在响应体中匹配的正则表达式。 |
filters[].replace | string | 是 | — | 替换内容。 |
filters[].scope | string | 否 | "once" | "once" 表示仅替换首次匹配,"global" 表示替换所有匹配。 |
filters[].options | string | 否 | "jo" | 正则表达式选项。参见 ngx.re.match。 |
互斥关系:body 和 filters 不能同时使用。
分步指南:在路由上启用 response-rewrite
1. 添加安全响应头
为路由添加安全响应头:
a6 route create -f - <<'EOF'
{
"id": "security-headers",
"uri": "/api/*",
"plugins": {
"response-rewrite": {
"headers": {
"set": {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains"
},
"remove": ["Server", "X-Powered-By"]
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"backend:8080": 1
}
}
}
EOF
2. 自定义错误响应体
a6 route create -f - <<'EOF'
{
"id": "custom-error",
"uri": "/maintenance/*",
"plugins": {
"response-rewrite": {
"status_code": 503,
"body": "{\"error\": \"Service under maintenance\", \"retry_after\": 300}",
"headers": {
"set": {
"Content-Type": "application/json",
"Retry-After": "300"
}
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"backend:8080": 1
}
}
}
EOF
3. 条件重写(仅针对 200 响应)
a6 route create -f - <<'EOF'
{
"id": "conditional-rewrite",
"uri": "/api/*",
"plugins": {
"response-rewrite": {
"headers": {
"set": {
"Cache-Control": "public, max-age=3600"
}
},
"vars": [["status", "==", 200]]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"backend:8080": 1
}
}
}
EOF
常见模式
正则响应体过滤器(全局替换文本)
使用公开 URL 替换响应体中的内部主机名:
{
"plugins": {
"response-rewrite": {
"filters": [
{
"regex": "http://internal\\.service\\.local",
"scope": "global",
"replace": "https://api.example.com"
}
]
}
}
}
多个正则过滤器
{
"plugins": {
"response-rewrite": {
"filters": [
{
"regex": "X-Amzn-Trace-Id",
"scope": "global",
"replace": "X-Trace-Id"
},
{
"regex": "\"debug\":\\s*true",
"scope": "global",
"replace": "\"debug\": false"
}
]
}
}
}
Base64 响应体(提供二进制内容)
{
"plugins": {
"response-rewrite": {
"status_code": 200,
"body": "SGVsbG8gV29ybGQ=",
"body_base64": true,
"headers": {
"set": {
"Content-Type": "text/plain"
}
}
}
}
}
返回解码后的响应体:Hello World
添加动态服务器信息请求头
{
"plugins": {
"response-rewrite": {
"headers": {
"set": {
"X-Served-By": "$balancer_ip:$balancer_port",
"X-Request-Id": "$request_id"
}
}
}
}
}