PII Detection and Redaction
The PII guardrail detects sensitive data in request and response text. It can mask each match with a redaction token and let traffic continue, or block the request or response.
PII detection runs inside the gateway, so no external moderation service is called. Matched values are not written to gateway logs, usage records, or error responses.
In this guide, you will create a PII guardrail that masks built-in detector matches, add a custom pattern, and verify both masking and blocking behavior.
Prerequisites
Before starting, prepare the following:
- Review Guardrail Behavior for hook points and enforcement modes.
- One of these configuration paths:
- AISIX Cloud with an environment, an attached gateway, and a write-scoped admin token. For On-Premises, follow the AISIX Cloud Quickstart. To request Hybrid Cloud access, contact API7.
- An open-source AISIX gateway that loads a declarative
resources.yamlfile.
- A working model alias and caller API key that can send Chat Completions requests.
curl. The AISIX Cloud path also usesjq.
Built-in Detectors
Enable any of the built-in detectors by listing its type under detectors. Two detectors validate a checksum before matching so that a random digit run is not masked.
type | Matches |
|---|---|
email | Email addresses |
china_mobile | Mainland China mobile numbers |
china_id_card | Mainland China resident ID numbers (ISO 7064 checksum) |
bank_card | Bank card numbers (Luhn checksum) |
us_ssn | US Social Security Numbers |
ip_address | IPv4 addresses |
api_key | API keys and tokens (OpenAI, AWS, GitHub, Slack, Google signatures) |
jwt | JSON Web Tokens |
private_key | PEM private key blocks |
Match Actions
Each match resolves to one of two actions:
mask: replace the matched span with a token such as[EMAIL_REDACTED]. Input masking continues to the upstream model; output masking continues to the caller.block: reject the request or response with422 Unprocessable Entity.
default_action sets the action for every detector and custom pattern. Set action on an individual detector or custom pattern to override it.
Create a PII Guardrail
The example below masks email addresses and API keys in caller requests before AISIX sends them to the upstream provider. Choose one configuration path, then use the shared verification procedure.
Export the gateway values used by both paths:
# AISIX_PROXY has no trailing slash or endpoint path.
# The local quickstarts use http://127.0.0.1:3000.
export AISIX_PROXY="YOUR_AISIX_GATEWAY_URL"
export AISIX_API_KEY="YOUR_CALLER_API_KEY"
export AISIX_MODEL="gpt-4o-mini"
AISIX Cloud
Export the control-plane connection details:
# AISIX_CP includes /api and has no trailing slash.
# The local On-Premises quickstart uses http://localhost:8080/api.
export AISIX_CP="YOUR_AISIX_CLOUD_ADMIN_API_BASE_URL"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
Create an input guardrail that masks the email and api_key detectors, and capture its ID:
GUARDRAIL_ID=$(curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/guardrails" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "pii-redaction-policy",
"enabled": false,
"hook_point": "input",
"kind": "pii",
"config": {
"default_action": "mask",
"detectors": [
{ "type": "email" },
{ "type": "api_key" }
]
}
}' | jq -r '.guardrail.id')
❶ Creating the guardrail disabled prevents it from applying globally before its attachment exists. Enable it after attaching it below.
❷ input masks the caller request before AISIX sends it upstream. Use output to redact provider responses, or both to cover both sides. See Guardrail Hook Point.
❸ default_action sets the action for listed detectors. mask replaces each match with a redaction token. Set a detector-level action only when one detector needs to behave differently.
Attach the guardrail to the whole environment (scope_type: "env" binds every request in the environment and omits scope_id; use model, api_key, or team with a scope_id to narrow it):
curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/guardrails/$GUARDRAIL_ID/attachments" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "scope_type": "env" }'
Enable the guardrail after its attachment exists:
curl -sS -X PATCH "$AISIX_CP/environments/$ENV_ID/guardrails/$GUARDRAIL_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": true}'
The enabled configuration projects to attached gateways automatically.
Open-Source AISIX Gateway
Add the guardrail to the resources file that already defines the example model and caller API key:
guardrails:
- name: pii-redaction-policy
enabled: true
hook_point: input
kind: pii
default_action: mask
detectors:
- type: email
- type: api_key
guardrail_attachments:
- guardrail_id: pii-redaction-policy
scope_type: env
priority: 100
❶ mask replaces every detected span with a redaction token and lets the request continue.
❷ Each detector runs inside the gateway. An individual detector can set action: block when it should override the default.
A guardrail applies only where an attachment puts it: add a guardrail_attachments entry naming it, or it loads and inspects no traffic. Validate the complete file, then reload the gateway. See Reload a Resources File for the runnable Docker workflow.
For response-side PII redaction on streamed traffic, AISIX holds response content until it can apply the guardrail. See Streaming Output for buffer defaults and overflow behavior.
Verify Masking
Send a request whose prompt contains an email address. AISIX Cloud projection is asynchronous; if the first request does not reflect the new rule, wait for the gateway to apply the latest revision and retry. See Resource Projection for convergence checks.
curl -sSi -X POST "$AISIX_PROXY/v1/chat/completions" \
-H "Authorization: Bearer ${AISIX_API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"model": "${AISIX_MODEL}",
"messages": [
{
"role": "user",
"content": "email me at alice@example.com about the order"
}
]
}
EOF
The request succeeds with HTTP/1.1 200 OK. AISIX rewrites the prompt before calling the upstream model, so the provider receives the masked prompt:
email me at [EMAIL_REDACTED] about the order
The original value does not reach the upstream provider, the gateway log, or the usage record. Usage records carry per-detector match counts, including detector names but not matched text.
Block Sensitive Data
Use block when a detector should reject traffic instead of masking it. The following examples add a second guardrail that blocks requests containing a valid mainland China resident ID.
AISIX Cloud
Create the blocking guardrail and attach it to the environment:
BLOCK_GUARDRAIL_ID=$(curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/guardrails" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "block-id-card",
"enabled": false,
"hook_point": "input",
"kind": "pii",
"config": {
"default_action": "block",
"detectors": [
{ "type": "china_id_card" }
]
}
}' | jq -r '.guardrail.id')
curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/guardrails/$BLOCK_GUARDRAIL_ID/attachments" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "scope_type": "env" }'
curl -sS -X PATCH "$AISIX_CP/environments/$ENV_ID/guardrails/$BLOCK_GUARDRAIL_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"enabled": true}'
Open-Source AISIX Gateway
Add the blocking guardrail alongside the masking guardrail in resources.yaml:
guardrails:
- name: pii-redaction-policy
enabled: true
hook_point: input
kind: pii
default_action: mask
detectors:
- type: email
- type: api_key
- name: block-id-card
enabled: true
hook_point: input
kind: pii
default_action: block
detectors:
- type: china_id_card
guardrail_attachments:
- guardrail_id: pii-redaction-policy
scope_type: env
priority: 100
- guardrail_id: block-id-card
scope_type: env
priority: 100
Validate the complete file and reload the gateway.
Verify Blocking
A request whose content includes a valid ID number is rejected:
curl -sSi -X POST "$AISIX_PROXY/v1/chat/completions" \
-H "Authorization: Bearer ${AISIX_API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"model": "${AISIX_MODEL}",
"messages": [
{
"role": "user",
"content": "my id is 11010519491231002X"
}
]
}
EOF
A blocked response starts with HTTP/1.1 422 Unprocessable Entity and includes this body:
{
"error": {
"message": "request blocked by content policy (guardrail 'block-id-card')",
"type": "content_filter"
}
}
The message is deliberately generic and does not echo the matched value.
Add a Custom Pattern
Add custom_patterns to detect data specific to your organization. Each pattern needs a name and a Rust-compatible regex, and accepts an optional per-pattern action and replacement. AISIX uses the name in the redaction token and match counts. An invalid expression prevents the guardrail from entering the active chain.
The following examples add an employee ID pattern to pii-redaction-policy while preserving its existing email and API-key detectors.
AISIX Cloud
Replace the existing guardrail's configuration with the expanded detector set:
curl -sS -X PATCH "$AISIX_CP/environments/$ENV_ID/guardrails/$GUARDRAIL_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"config": {
"default_action": "mask",
"detectors": [
{ "type": "email" },
{ "type": "api_key" }
],
"custom_patterns": [
{
"name": "employee_id",
"regex": "EMP-[0-9]{6}"
}
]
}
}'
The existing attachment remains in place when the guardrail configuration changes.
Open-Source AISIX Gateway
Add the same pattern to the existing guardrail entry:
guardrails:
- name: pii-redaction-policy
enabled: true
hook_point: input
kind: pii
default_action: mask
detectors:
- type: email
- type: api_key
custom_patterns:
- name: employee_id
regex: 'EMP-[0-9]{6}'
Validate the complete file and reload the gateway. A match on this pattern is masked as [EMPLOYEE_ID_REDACTED] through either configuration path.
Choose What a Masked Span Becomes
By default a masked span becomes [<NAME>_REDACTED], built from the pattern's name. Set replacement on a pattern to choose the text yourself, or set it to an empty string to delete the span entirely.
custom_patterns:
- name: employee_id
regex: 'EMP-[0-9]{6}'
replacement: '******'
Two rules decide what the result looks like, and both differ from what a regular-expression tool usually does:
replacementis used literally.$1and other group references are not expanded. This is the opposite ofurl_rewriteson a passthrough route, where$1does expand.- A regex with a capture group replaces only group 1. The rest of the match is kept as it was. A regex with no capture group replaces the whole match.
The second rule is how a pattern says "replace the value, keep the key." This masks the version but keeps the field name:
custom_patterns:
- name: eda_version
regex: 'version: (\S+)'
replacement: '***'
version: 12.1 becomes version: ***.
Putting the surrounding text into replacement as well is the common mistake, and nothing rejects it:
# Wrong: the regex already keeps `ACCT-` and `-5678`, and `$1` is literal.
custom_patterns:
- name: account
regex: 'ACCT-([0-9]{4})-([0-9]{4})'
replacement: 'ACCT-$1-****'
ACCT-1234-5678 becomes ACCT-ACCT-$1-****-5678 — accepted, and still shaped like something redacted. Keep context outside the capture group instead:
custom_patterns:
- name: account
regex: 'ACCT-([0-9]{4})-([0-9]{4})'
replacement: '****'
ACCT-1234-5678 becomes ACCT-****-5678.
replacement applies to mask only. A pattern whose effective action is block rejects the request instead of rewriting it, so sending both is an error.
Decide What Happens When AISIX Cannot Scan
The PII guardrail runs inside the gateway and never calls out, so no backend can fail for it. It can still be handed a request body it cannot read — one that is not valid UTF-8. AISIX refuses that request rather than forwarding it unmasked and unscanned, and fail_open is the setting that decides.
fail_open defaults to false: content the guardrail could not be offered is refused with 422, and the message names the failure tag unscannable_body. Set it to true where availability matters more than enforcement. The pass is still recorded on the request's usage record as a bypass, so you can alert on it.
In AISIX Cloud, the option is on the guardrail's create and edit forms. In a resources file, set it on the guardrail entry:
guardrails:
- name: pii-redaction-policy
kind: pii
fail_open: true
default_action: mask
detectors:
- type: email
- type: api_key
Two limits are worth knowing before you rely on it. This kind has no output_fail_open counterpart, so the one value governs both of its hooks — it is separate from on_buffer_exceeded, which decides what happens when a held-back response outgrows max_buffer_bytes. And fail_open does not reach a streamed response that is already being held back: a PII guardrail on the output hook buffers streamed output so it can mask across chunk boundaries, and a stream left with nothing scannable is then refused whatever fail_open says.
Earlier gateways ignored this field on pii entirely. A rule that carries fail_open: true today, set when the field did nothing, changes behavior as soon as the gateway is upgraded. See When a Guardrail Cannot Complete Its Check.
Next Steps
You have configured the built-in PII guardrail and verified mask and block behavior. Use these guides to refine or expand the policy:
- Guardrail Behavior: adjust hook points, enforcement mode, or streaming output.
- Presidio Guardrails: use NER entities and anonymization operators from a Presidio service you run.
- Choosing a Guardrail Provider: compare built-in PII detection with remote guardrail services.