Conditionally Disable Global Plugins
Global plugins apply to all routes by default, which is useful for enforcing organization-wide policies such as logging, rewriting, or traffic controls. However, some routes may need to be exempt from certain global plugins.
This guide walks you through how to conditionally disable global plugins for specific routes using route labels and the _meta.filter mechanism.
How It Works
The approach uses three components:
- Route labels: A label (e.g.,
api7_disable_global_rules: response-rewrite) on routes that should skip certain global plugins. serverless-pre-function: A global serverless function that reads the route label and registers it as a custom NGINX variable._meta.filter: A condition on the global plugin that evaluates the variable and skips execution when it matches.
Prerequisites
- An API7 Enterprise instance is running.
- A Gateway Group is created and a Gateway instance is running.
- A token from the Dashboard.
Option 1: Regex Match (Recommended)
This approach returns the raw route label string and uses a regex filter on the global plugin. It was validated end to end in the local API7 Gateway environment with a global response-rewrite rule.
Step 1: Create the Serverless Function
Configure the serverless-pre-function as a global plugin that registers a custom variable from the route label:
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/global_rules/serverless-pre-function?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function(conf, ctx) local core = require \"apisix.core\" core.ctx.register_var(\"api7_disable_global_rules\", function(ctx) local route = ctx.matched_route and ctx.matched_route.value if route and route.labels then return route.labels.api7_disable_global_rules end return 1 end) end"
]
}
}
}'
global_rules:
serverless-pre-function:
phase: rewrite
functions:
- |-
return function(conf, ctx)
local core = require "apisix.core"
core.ctx.register_var("api7_disable_global_rules", function(ctx)
local route = ctx.matched_route and ctx.matched_route.value
if route and route.labels then
return route.labels.api7_disable_global_rules
end
return 1
end)
end
adc sync -f adc.yaml
Step 2: Configure the Global Plugin with _meta.filter
Add _meta.filter to the global plugin you want to conditionally skip. The following example applies a response header globally unless the route label contains response-rewrite:
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/global_rules/response-rewrite?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"plugins": {
"response-rewrite": {
"headers": {
"set": {
"X-Global-Rule": "applied"
}
},
"_meta": {
"filter": [
["api7_disable_global_rules", "!", "~~", ".*response-rewrite.*"]
]
}
}
}
}'
global_rules:
response-rewrite:
headers:
set:
X-Global-Rule: applied
_meta:
filter:
- - api7_disable_global_rules
- "!"
- "~~"
- ".*response-rewrite.*"
adc sync -f adc.yaml
Step 3: Label Routes to Skip the Plugin
Add the api7_disable_global_rules label to routes that should bypass the global plugin.
- Admin API
- ADC
# 1. Create a Service for the routes
curl -k "https://localhost:7443/apisix/admin/services/global-exemption-service?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "global-exemption-service",
"upstream": {
"type": "roundrobin",
"nodes": [
{
"host": "httpbin.org",
"port": 80,
"weight": 100
}
]
}
}'
# 2. Create a labeled route that skips the global plugin
curl -k "https://localhost:7443/apisix/admin/routes/global-exempt-route?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "global-exempt-route",
"paths": ["/anything/global-exempt"],
"methods": ["GET"],
"labels": {
"api7_disable_global_rules": "response-rewrite"
},
"service_id": "global-exemption-service"
}'
# 3. Create a normal route where the global plugin still applies
curl -k "https://localhost:7443/apisix/admin/routes/global-protected-route?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "global-protected-route",
"paths": ["/anything/global-protected"],
"methods": ["GET"],
"service_id": "global-exemption-service"
}'
services:
- name: global-exemption-service
upstream:
nodes:
- host: httpbin.org
port: 80
weight: 1
routes:
- name: global-exempt-route
uris:
- /anything/global-exempt
labels:
api7_disable_global_rules: response-rewrite
- name: global-protected-route
uris:
- /anything/global-protected
adc sync -f adc.yaml
Verify
Send a request to the labeled route:
curl -i "http://127.0.0.1:9080/anything/global-exempt"
The response should not include the X-Global-Rule header because response-rewrite is skipped.
Send a request to the non-labeled route:
curl -i "http://127.0.0.1:9080/anything/global-protected"
The response should include X-Global-Rule: applied because the global plugin is active.
Option 2: List-Based Match
This approach returns a list of plugin names from the label and uses the has operator in _meta.filter.
Complex regex expressions may incur more processing overhead compared to the list-based approach.
Serverless Function
Use this variant when you prefer exact list membership checks for comma-separated labels:
global_rules:
serverless-pre-function:
phase: rewrite
functions:
- >-
return function(conf, ctx)
local core = require "apisix.core"
core.ctx.register_var("api7_disable_global_rules", function(ctx)
local route = ctx.matched_route and ctx.matched_route.value
if route and route.labels then
local value = route.labels.api7_disable_global_rules
if value then
local disable_plugins = {}
for plugin_name in string.gmatch(value, "[^,]+") do
local trimmed_name = string.gsub(plugin_name, "^%s*(.-)%s*$", "%1")
if trimmed_name and trimmed_name ~= "" then
table.insert(disable_plugins, trimmed_name)
end
end
return disable_plugins
end
end
return 1
end)
end
Global Plugin with has Filter
Use the has operator to check whether the returned list contains the plugin name:
global_rules:
response-rewrite:
_meta:
filter:
- - api7_disable_global_rules
- "!"
- has
- response-rewrite
headers:
set:
X-Global-Rule: applied
Understanding _meta.filter
The _meta field is available on every plugin and supports the following options:
| Field | Type | Description |
|---|---|---|
disable | boolean | Disable the plugin entirely |
error_response | string/object | Custom error response when the plugin rejects a request |
priority | integer | Override the plugin's execution priority |
filter | array | Runtime expression; the plugin is skipped if the expression evaluates to false |
The filter field uses the same expression syntax as route matching (radixtree vars). Supported operators include ==, !=, ~~ (regex), has (list contains), and more.