Response Transforms
Overview
Response Transforms are per-backend rules that rewrite, inject, or mask response body content via a Rust Lua filter. Response body manipulation is not possible with HAProxy’s native http-response rules (which only handle headers), so this feature uses the HAProxy filter API.
Transform Types
Replace
Regex find/replace with $1 backreferences. Scoped per backend and filtered by content type.
Find: <title>(.*?)</title>
Replace: <title>Acme - $1</title>
Inject
Insert a string before, after, or in place of a regex anchor. Common use case: inject a script or analytics tag before </body>.
Anchor: </body>
Position: before
Inject: <script src="/analytics.js"></script>
Mask
Detect sensitive data in the response body and replace it with a token. Two masking modes:
- tokenize: Store token-to-original mapping in Valkey with a TTL. Supports bidirectional unmask (for backend services that need the original value).
- encrypt: AES-256-GCM encryption with a key from an environment variable. No external storage needed.
Built-in PII Detectors
| Detector | Detects |
|---|---|
email | Email addresses |
phone | Phone numbers |
ssn | Social Security Numbers |
credit_card | Credit card numbers |
ip | IP addresses |
You can also use custom regex patterns for domain-specific sensitive data.
Configuration
| Property | Description |
|---|---|
name | Display name |
backend_id / backend_ids | Per-backend scoping |
priority | Ordered (drag-and-drop) |
enabled | Toggle without deleting |
transform_type | replace, inject, or mask |
content_types | Comma-separated MIME prefixes to process |
max_body_size | Skip if body is larger (default 1MB) |
find_regex | Regex for replace/mask |
replace_string | Replacement for replace |
inject_string | String to inject |
inject_position | before, after, or replace |
mask_mode | regex or detector |
detector | email, phone, ssn, credit_card, ip |
token_mode | tokenize or encrypt |
token_prefix | Prefix for tokens (e.g. MASK_) |
token_ttl | TTL for tokenize mode (seconds) |
encrypt_key_env | Environment variable name for encrypt key |
Feature Gating
Response Transforms are gated behind the resp_transform_enabled feature flag. Enable it in Settings > Global Options before configuring transforms.
Filter Ordering
The filter pipeline order is:
cache -> resp_transform -> compression
Transforms run before compression so that compression compresses the transformed output. Transforms run after cache so that the cache stores the raw body (and the transform is applied on each cache hit).
HA Recommendation
For high-availability deployments, encrypt mode is recommended because it is stateless — no external Valkey dependency. Use tokenize mode when you need TTL-based expiry or per-token revocation.
Fail-to-Encrypt Fallback
When using tokenize mode and Valkey is unreachable during masking, the transform automatically falls back to AES-256-GCM encryption. This ensures PII is still masked even if the token store is temporarily unavailable.
FCGI Limitation
The response transform filter is skipped for FastCGI backends due to an HAProxy 3.4 bug.
Step-by-Step: Replace Text in Responses
- Navigate to Settings > Global Options and enable
resp_transform_enabled - Navigate to Performance > Response Transforms
- Click Add Transform
- Name:
replace-title - Backend: select your backend
- Type: replace
- Content types:
text/html - Find regex:
<title>(.*?)</title> - Replace:
<title>My Site - $1</title> - Save and Apply
Step-by-Step: Inject a Script
- Navigate to Performance > Response Transforms
- Click Add Transform
- Name:
inject-analytics - Backend: select your backend
- Type: inject
- Content types:
text/html - Anchor regex:
</body> - Position: before
- Inject string:
<script src="/analytics.js" defer></script> - Save and Apply
Step-by-Step: Mask PII (Encrypt Mode)
- Navigate to Settings > Global Options and enable
resp_transform_enabled - Set an encryption key in your environment:
RESP_TRANSFORM_KEY=<32-byte-hex-key> - Navigate to Performance > Response Transforms
- Click Add Transform
- Name:
mask-emails - Backend: select your backend
- Type: mask
- Mask mode: detector
- Detector: email
- Token mode: encrypt
- Token prefix:
MASKED_ - Encrypt key env:
RESP_TRANSFORM_KEY - Save and Apply
The encryption key must be a 32-byte (256-bit) value. Generate one with openssl rand -hex 32 and set it as an environment variable. The same key must be used across all coreX Manager instances for consistent masking.
Verification
-
Test replace:
curl -k https://localhost/ | grep '<title>'Expect the replaced title.
-
Test inject:
curl -k https://localhost/ | grep 'analytics.js'Expect the injected script before
</body>. -
Test mask:
curl -k https://localhost/api/usersEmail addresses should be replaced with
MASKED_prefixed tokens. -
Test unmask (tokenize mode):
- Use the unmask API endpoint with the token to retrieve the original value
- Verify the original value matches
Next Steps
- Compression — Compress transformed responses
- Caching — Cache responses (transforms apply on cache hit)
- Image Conversion — Convert images to WebP