The SecurePath connector adds Radware Cloud WAF and Bot Manager protection to Azure API Management (APIM). Every request your APIs serve is inspected by SecurePath before it reaches your backend, with allow / block / redirect verdicts enforced at the gateway. The connector is delivered as a single XML policy file that runs entirely inside APIM's policy pipeline — no custom C# code, no Functions, no sidecars, no separate compute.
| What you get | Where it shows up |
|---|---|
| WAF — SQL-injection / XSS / RFI / file-upload / path traversal blocking, plus the rest of the SecurePath rule set | HTTP 403 to the client with SecurePath's block page (HTML) or structured block JSON |
| Redirect / challenge — risk-based redirects to a SecurePath-issued challenge URL | HTTP 301/302 to the client |
Bot Manager — BM cookie propagation (__uzma, __uzmf, uzmcr, etc.) and ShieldSquare-Response forwarding on every verdict path |
Set-Cookie and BM headers on the client response (allow, block, redirect alike) |
Response-phase analytics (v2) — fire-and-forget log POST after every response, with disposition header (allowed / blocked) |
Visible in the Radware Cloud "events" view; near-zero client-visible latency |
Reserved header security — rejects (HTTP 403) any client request that contains spoofed x-rdwr-* headers |
403 returned at the gateway, request never forwarded |
| Static-asset bypass — configured methods + extensions skip inspection | Lower per-request cost on cacheable static traffic |
| Fail-open by default — if SecurePath is unreachable, traffic continues to your backend | Configurable via Named Values |
Is this for you? If you run Azure API Management (any tier — Developer, Basic, Standard, Premium, v2, with caveats for Consumption) and have (or want) a Radware Cloud SecurePath subscription. If your gateway is something else — Kong, NGINX, MuleSoft Flex Gateway, F5, HAProxy, Envoy — Radware ships a separate connector for each; this repo is APIM only.
What APIM does NOT do — JavaScript injection. APIM XML policies cannot modify response bodies, so the Bot Manager browser-fingerprinting JS injection that other SecurePath connectors do is not available here. This is rarely an issue: APIM is an API gateway, so the traffic is typically JSON / XML / gRPC consumed by backend services, not HTML rendered in browsers. Bot Manager cookie propagation (the part that matters for non-browser clients) works in full.
┌──────────────────┐
│ Client request │ (Host: api.example.com, /v1/orders, ...)
└────────┬─────────┘
│ HTTPS (Azure-terminated)
▼
┌──────────────────────────────────────────┐
│ Azure API Management gateway │
│ │
│ ┌────────────────────────────────────┐ │
│ │ <inbound> SecurePath policy │ │ ┌────────────────┐
│ │ • build sideband request │ │ │ SecurePath │
│ │ • send-request to SP ──────────┼──┼─────▶│ Cloud │
│ │ • parse verdict ◀─────────┼──┼──────│ endpoint │
│ │ • allow / block / redirect │ │ └────────────────┘
│ └────────────────────────────────────┘ │ ▲
│ │ │ async
│ ┌────────────────────────────────────┐ │ │ log POST
│ │ <outbound> response-phase log │ │ │
│ │ • send-one-way-request to SP ────┼──┼──────────────┘
│ └────────────────────────────────────┘ │
└─────────────────┬────────────────────────┘
│ allowed → forward
▼
┌──────────────────────────────────────────┐
│ Your backend (App Service, AKS, AWS, │
│ on-prem, function app, anything APIM │
│ can reach) │
└──────────────────────────────────────────┘
For each client request, APIM:
- Runs the SecurePath policy in the inbound pipeline.
- The policy assembles the SecurePath sideband headers (
x-rdwr-app-id,x-rdwr-api-key,x-rdwr-connector-ip, etc.) and dispatches an HTTPS sideband request to your SecurePath application's endpoint via APIM's<send-request>policy. - SecurePath returns a verdict (
allow,block,redirect, or challenge). - The policy enforces the verdict — forward to backend, return 403 with a block page, or 302 to a challenge URL.
- After the response is delivered to the client, the outbound pipeline fires an asynchronous v2 response-phase log POST to SecurePath via
<send-one-way-request>. This is fire-and-forget and adds no client-visible latency.
The policy is stateless. All configuration lives in APIM Named Values, read fresh on every request. Multi-region APIM, multi-zone APIM, scale-out APIM — all work the same way.
The fastest path from "we have an APIM instance" to "SecurePath is inspecting traffic." All commands assume Azure CLI; the Portal-equivalent steps are documented in the same section below.
You'll need: an APIM instance you can edit, an active SecurePath application in the Radware Cloud portal (with the Application ID, API key, and
*.oop.radwarecloud.netendpoint hostname handy), and Azure CLI signed in.
1. Upload the Radware CA chain so APIM can verify SecurePath's TLS cert. Both certs are required (root + intermediate). Use the Azure Portal — there is no az apim CLI command for CA certificates (Azure CLI does not currently expose this; PowerShell New-AzApiManagementSystemCertificate and ARM templates are the only programmatic options):
- Azure Portal → your APIM instance → Security → Certificates → CA certificates → + Add.
- Rename
certs/rdwr-root-ca.pemtordwr-root-ca.cerfirst — APIM's upload dialog only accepts.cerfiles. PEM and CER are the same Base64 X.509 format, so a rename is sufficient (no conversion needed). Set the certificate ID tordwr-root-r1and the Store to Trusted Root Certification Authorities. - Click + Add again, repeat for
certs/rdwr-intermediate-ca.pem→ rename to.cer, certificate IDrdwr-ca-1a1, Store Intermediate Certification Authorities.
Tier note: CA certificate upload is supported on Developer / Basic / Standard / Premium tiers only. Standard v2 / Premium v2 (the v2 tiers) and Consumption tier require trust to be configured per backend instead — see Microsoft's Add a Custom CA Certificate doc.
2. Create the three Named Values that identify your SecurePath app (the API Management policy expects these plus the 17 connector-configuration values from Step 2a — 20 Named Values total):
# Replace each <placeholder> with your value. The angle-brackets mark
# placeholders only — substitute the entire token (including the < and >)
# with your actual value before pasting. The strings are quoted so bash
# won't treat the angle-brackets as redirection if you paste literally.
RG="<your-resource-group>"
APIM="<your-apim-instance>"
az apim nv create -g "$RG" --service-name "$APIM" \
--named-value-id rdwr-app-ep-addr --display-name rdwr-app-ep-addr \
--value "<your-app-id>.oop.radwarecloud.net"
az apim nv create -g "$RG" --service-name "$APIM" \
--named-value-id rdwr-app-id --display-name rdwr-app-id \
--value "<your-app-id>"
az apim nv create -g "$RG" --service-name "$APIM" \
--named-value-id rdwr-api-key --display-name rdwr-api-key \
--secret true \
--value "<your-api-key>"2a. Create the 17 policy-config Named Values. The policy substitutes all 20 Named Values literally at upload time — if any are missing, the policy save will fail validation. Paste the bash loop below to create them with their recommended values:
# RG and APIM should still be set from Step 2 above. If your shell session
# has ended, re-set them here (same values as Step 2):
# RG="<your-resource-group>"
# APIM="<your-apim-instance>"
declare -a NV=(
"rdwr-app-ep-port=443"
"rdwr-app-ep-ssl=true"
"rdwr-app-ep-timeout-seconds=10"
"rdwr-body-max-size-bytes=100000"
"rdwr-partial-body-size-bytes=10240"
"rdwr-multipart-max-size-bytes=100000"
"rdwr-true-client-ip-header=x-forwarded-for"
"rdwr-api-base-path=/"
"rdwr-bot-manager-enabled=false"
"plugin-version-info=700-v1.3.2"
"static-extensions-enabled=true"
"static-list-of-methods-not-to-inspect=GET,HEAD"
"static-list-of-bypassed-extensions=png,jpg,css,js,gif,ico,svg,woff,woff2"
"static-inspect-if-query-string-exists=true"
"chunked-request-allowed-content-types=application/json,application/x-www-form-urlencoded"
"rdwr-inline-trusted-sources=##DISABLED##"
"rdwr-inline-headers-enabled=false"
)
for kv in "${NV[@]}"; do
name="${kv%%=*}"; value="${kv#*=}"
az apim nv create -g "$RG" --service-name "$APIM" \
--named-value-id "$name" --display-name "$name" --value "$value"
done⚠ Heads up — empty-string values:
rdwr-api-base-pathandrdwr-inline-trusted-sourceswould naturally be empty for most users (no path-prefix to strip; no IP allow-list). The Azure CLI rejects--value "", so the recommended values above use sentinels the policy recognises:/(the policy strips trailing slash, so/resolves to "no path stripping") and##DISABLED##(the policy treats##DISABLED##/disabled/off/false/none/~/-as disabled). Set them to your real base path or CIDR list if you actually want those features.
3. Apply the policy to the API you want to protect (typically all operations).
<your-api-id>is the resource name of an APIM API you've already created (e.g.proxy-all,orders-api) — not the SecurePath endpoint hostname (that goes into Step 2 asrdwr-app-ep-addr). List your APIs withaz apim api list -g "$RG" --service-name "$APIM" --query "[].name" -o tsv.
The Azure CLI does not expose policy upload via az apim api … (there is no policy subgroup). Use az rest against the canonical ARM REST API — az rest is part of core CLI and needs no extensions:
# Re-declare here in case you're running this snippet in a fresh shell.
# Replace each <placeholder> with your value (the angle-brackets are placeholders, not literal):
RG="<your-resource-group>"
APIM="<your-apim-instance>"
API_ID="<your-api-id>"
SUB=$(az account show --query id -o tsv)
URI="https://management.azure.com/subscriptions/$SUB/resourceGroups/$RG/providers/Microsoft.ApiManagement/service/$APIM/apis/$API_ID/policies/policy?api-version=2024-05-01"
# Wrap the policy XML into the JSON envelope APIM expects.
# format MUST be "rawxml" — the default "xml" rejects {{named-value}} references inside attributes.
jq -Rs '{properties: {format: "rawxml", value: .}}' \
rdwr-azureapim-securepath-connector-v1.3.xml \
> /tmp/apim-policy-body.json
az rest --method PUT --uri "$URI" \
--headers "Content-Type=application/json" \
--body @/tmp/apim-policy-body.jsonSuccess returns the stored policy contract (HTTP 201 on first apply, 200 on update). If you don't have jq (or are on Windows PowerShell), the Onboarding Walkthrough below shows PowerShell, Bicep, and Azure Portal alternatives.
4. Smoke-test:
# APIM is your APIM service name (set in Step 2). Re-set if your shell ended:
# APIM="<your-apim-instance>"
#
# Replace <api-path> with the path of the API you applied the policy to in Step 3
# (e.g. "orders" for an API created with `az apim api create --path orders`).
# Replace <subscription-key> with an APIM subscription key from your APIM instance
# (Portal → APIM → Subscriptions → "Built-in all-access" or any product key).
# This is the APIM subscription key — it is NOT the SecurePath rdwr-api-key from Step 2.
# Should pass — request reaches your backend
curl -i "https://$APIM.azure-api.net/<api-path>/health" \
-H "Ocp-Apim-Subscription-Key: <subscription-key>"
# Expected: HTTP 200 from your backend.
# Should be blocked — SQL-injection probe in query string
curl -i "https://$APIM.azure-api.net/<api-path>/?id=1' OR '1'='1" \
-H "Ocp-Apim-Subscription-Key: <subscription-key>"
# Expected: HTTP 403 with the SecurePath block page.If both checks pass, the connector is working end-to-end. The deeper walkthrough — every Named Value, deployment-shape caveats, configuration details, troubleshooting, and uninstall procedure — lives in Onboarding Walkthrough below.
The same policy XML works across every APIM deployment shape. Where the shapes differ is in what you need to do operationally to give APIM outbound network reach to SecurePath:
| APIM shape | Status | What you need to do |
|---|---|---|
| Developer / Basic / Standard / Premium (the v1 family) | ✅ Fully supported | Standard onboarding — ensure outbound 443 to *.oop.radwarecloud.net works (it does by default unless you've added route-table restrictions). |
| Standard v2 / Premium v2 | ✅ Fully supported | Same as v1. The v2 family uses an updated runtime but the same policy engine. |
| Consumption tier | Consumption-tier APIM imposes policy-expression and execution limits — large-body buffering is limited and some advanced expressions are not available. The policy degrades gracefully (sideband and verdict enforcement still work; some edge-case headers and the inline-bypass IP allow-list are skipped). Test in Consumption before committing to it for production. | |
| Self-hosted gateway | ✅ Supported | The Self-Hosted Gateway uses the same policy engine as cloud APIM. The <send-one-way-request> log POST runs from the self-hosted node — make sure that node has outbound 443 to *.oop.radwarecloud.net and the Radware CA in its trust store. |
| Internal VNet (stv2 / Premium internal mode) | ✅ Supported | The APIM gateway must have outbound network access to *.oop.radwarecloud.net. If your subnet route-tables / Azure Firewall / NSGs force-tunnel internet egress, explicitly allow the SecurePath endpoint FQDN. The Microsoft.Web service tag is not sufficient — SecurePath endpoints don't sit inside that tag's IP ranges. |
| Custom domains and backend mTLS | ✅ Independent | The connector operates on the APIM-side request/response pipeline. Backend mTLS, custom domains, and gateway certificates do not interact with the SecurePath sideband. |
The sideband request adds approximately 20–80 ms of synchronous overhead per request (median ~30 ms; p99 < 100 ms over public-internet egress to the closest SecurePath PoP). The default rdwr-app-ep-timeout-seconds is 10, so a slow PoP causes at most a 10-second stall before the policy fails open and forwards the request to the backend.
The response-phase log uses <send-one-way-request>, which is fire-and-forget — it adds no measurable client-visible latency on the response path.
| Path | Purpose |
|---|---|
rdwr-azureapim-securepath-connector-v1.3.xml |
The policy XML you apply to your APIM API |
release-notes.md |
Per-version release notes — what changed, when |
certs/rdwr-root-ca.pem |
Radware Root CA — upload to APIM CA store |
certs/rdwr-intermediate-ca.pem |
Radware Intermediate CA — upload to APIM CA store |
certs/rdwr-ca-chain.pem |
Combined chain (informational; APIM needs them uploaded individually) |
certs/README.md |
CA upload guide for Portal / CLI / ARM-Bicep |
The full step-by-step. Pick the path that matches your tooling.
The SecurePath endpoint (*.oop.radwarecloud.net) is signed by a private Radware CA. APIM must trust the chain before sideband calls will succeed.
Important — Azure CLI does NOT support APIM CA certificate upload. The az apim command tree has no certificate or certificate-authority subcommand. You must use the Portal, PowerShell, or ARM/Bicep. (Earlier versions of these docs incorrectly suggested an az apim certificate create command — that command has never existed in az apim. The only available paths are below.)
Tier prerequisite: This step applies to Developer / Basic / Standard / Premium tiers (the v1 family). If your APIM is on Standard v2 / Premium v2 or Consumption tier, the CA-certificates page is unavailable; trust is configured per-backend instead — see Microsoft's Add a Custom CA Certificate doc.
Path A — Azure Portal (recommended):
- Rename
certs/rdwr-root-ca.pemtocerts/rdwr-root-ca.cerandcerts/rdwr-intermediate-ca.pemtocerts/rdwr-intermediate-ca.cer. PEM and CER are the same Base64 X.509 format; APIM's upload dialog filters by extension and accepts only.cer(no conversion needed, just rename). - Open the Azure Portal → your APIM instance → Security → Certificates → CA certificates → + Add.
- Upload
rdwr-root-ca.cer: certificate IDrdwr-root-r1, Store = Trusted Root Certification Authorities. Password field is optional (leave blank — only the public key is needed). Save. - Click + Add again. Upload
rdwr-intermediate-ca.cer: certificate IDrdwr-ca-1a1, Store = Intermediate Certification Authorities. Save.
Path B — PowerShell: Microsoft documents New-AzApiManagementSystemCertificate for this — see Microsoft's CA certificate doc.
Path C — ARM / Bicep: an example template is in certs/README.md for IaC pipelines.
Both certificates are required. The provisioning step ("CA certificate update in progress") can take 15+ minutes on larger instances.
The policy reads all configuration from APIM Named Values. Create them via the Azure Portal, Azure CLI, or your IaC tool of choice.
Your SecurePath app credentials (3 values) — set these from your Radware Cloud portal:
| Named Value | Example | Secret? | Description |
|---|---|---|---|
rdwr-app-ep-addr |
your-app-id.oop.radwarecloud.net |
— | SecurePath endpoint hostname |
rdwr-app-id |
your-app-id |
— | Application ID from the Radware Cloud portal |
rdwr-api-key |
your-api-key |
✓ | API key from the Radware Cloud portal — always create with --secret true |
Connector configuration (17 values) — set each to the recommended value below (the policy doesn't fall back if you skip them):
All 20 Named Values must exist. The policy uses
{{name}}substitution at policy-save time — if any referenced Named Value is missing, APIM rejects the policy upload with a "Named Value 'rdwr-...' not found" error. The "Recommended value" column below is the literal string to set as the Named Value's value, not a fallback that's auto-applied if you skip the Named Value.
| Named Value | Recommended value | Secret? | Description |
|---|---|---|---|
rdwr-app-ep-port |
443 |
— | SecurePath endpoint port |
rdwr-app-ep-ssl |
true |
— | Use HTTPS for sideband requests |
rdwr-app-ep-timeout-seconds |
10 |
— | Sideband request timeout (seconds) |
rdwr-body-max-size-bytes |
100000 |
— | Max request body forwarded to SecurePath |
rdwr-partial-body-size-bytes |
10240 |
— | Partial body size for oversized chunked requests |
rdwr-multipart-max-size-bytes |
100000 |
— | Max multipart form-data body size |
rdwr-true-client-ip-header |
x-forwarded-for |
— | Header containing the real client IP |
rdwr-api-base-path ⚠ |
/ (see callout below) |
— | API base path to strip from sideband URI (e.g., /api) |
rdwr-bot-manager-enabled |
false |
— | Enable Bot Manager cookie and header handling |
plugin-version-info |
700-v1.3.2 |
— | Plugin version string sent in x-rdwr-plugin-info |
static-extensions-enabled |
true |
— | Enable static resource bypass |
static-list-of-methods-not-to-inspect |
GET,HEAD |
— | HTTP methods eligible for static bypass |
static-list-of-bypassed-extensions |
png,jpg,css,js,gif,ico,svg,woff,woff2 |
— | File extensions to bypass |
static-inspect-if-query-string-exists |
true |
— | Force inspection when a query string is present |
chunked-request-allowed-content-types |
application/json,application/x-www-form-urlencoded |
— | Content types eligible for chunked body forwarding |
rdwr-inline-trusted-sources ⚠ |
##DISABLED## (see callout below) |
— | Optional inline-bypass IP allow-list |
rdwr-inline-headers-enabled |
false |
— | Optional inline-bypass header signature mode |
⚠ Empty-string trap. Two of these Named Values would naturally be left empty (
rdwr-api-base-pathif you have no path-prefix to strip;rdwr-inline-trusted-sourcesif you don't want an IP allow-list).az apim nv create --value ""rejects empty strings — the create call fails and the Named Value never gets created, which then fails the policy upload. The policy is built to recognise sentinel-disable tokens for both:
rdwr-api-base-path: set to/to mean "no path stripping". The policy strips the trailing/, resolving to empty internally — same effect as a true empty value, but the CLI accepts it. If your API actually does sit under a base path (e.g. all operations under/api/v1), set this to that path.rdwr-inline-trusted-sources: set to##DISABLED##to disable the inline-bypass IP allow-list. The policy treats##DISABLED##/disabled/off/false/none/~/-(case-insensitive) as disabled. If you do want an allow-list, set this to a comma-separated list of CIDRs and/or single IPs (e.g.,10.0.0.0/16,1.2.3.4).
About marking secrets: only
rdwr-api-keyshould be marked as a Secret Named Value (--secret true). Plain Named Values are returned byaz apim nv showdirectly and visible in the Portal — useful for operational checks. Secret Named Values are encrypted at rest, masked as••••in the Portal display, and requireaz apim nv show-secret(a separate command) to read back. Note: APIM's API Inspector trace shows the resolved Named Value at policy-execution time, so Secret Named Values are not masked in trace output once a policy stamps them into a header / URL / body. For production, restrict tracing-enabled subscriptions or storerdwr-api-keyin Azure Key Vault (Microsoft's recommended option — supports automatic four-hour rotation).
If you ran the Quickstart, the 3 SecurePath-app-credential Named Values are already created. Paste the script below for the remaining 17. Set RG and APIM first.
RG=<your-resource-group>
APIM=<your-apim-instance>
# Connector configuration — Named Values the policy substitutes literally; create each with the recommended value.
declare -a NV=(
"rdwr-app-ep-port=443"
"rdwr-app-ep-ssl=true"
"rdwr-app-ep-timeout-seconds=10"
"rdwr-body-max-size-bytes=100000"
"rdwr-partial-body-size-bytes=10240"
"rdwr-multipart-max-size-bytes=100000"
"rdwr-true-client-ip-header=x-forwarded-for"
"rdwr-api-base-path=/"
"rdwr-bot-manager-enabled=false"
"plugin-version-info=700-v1.3.2"
"static-extensions-enabled=true"
"static-list-of-methods-not-to-inspect=GET,HEAD"
"static-list-of-bypassed-extensions=png,jpg,css,js,gif,ico,svg,woff,woff2"
"static-inspect-if-query-string-exists=true"
"chunked-request-allowed-content-types=application/json,application/x-www-form-urlencoded"
"rdwr-inline-trusted-sources=##DISABLED##"
"rdwr-inline-headers-enabled=false"
)
for kv in "${NV[@]}"; do
name="${kv%%=*}"
value="${kv#*=}"
az apim nv create -g "$RG" --service-name "$APIM" \
--named-value-id "$name" --display-name "$name" \
--value "$value"
doneIf a Named Value already exists from an earlier attempt, az apim nv create returns "NamedValue with the specified id already exists" — switch that one Named Value to az apim nv update for the value you want.
az apim nv list -g "$RG" --service-name "$APIM" --query "[].{name:name,secret:secret}" -o tableExpect 20 rows total (3 SecurePath-app credentials + 17 connector configuration). If any are missing, the policy upload in Step 3 will fail validation. If you see extras with the rdwr- prefix that aren't in the tables above, those are stale from an earlier release and can be deleted with az apim nv delete -g "$RG" --service-name "$APIM" --named-value-id <name>.
Apply the XML policy at the API level (covers all operations) or per-operation as required.
Via Azure Portal:
- Navigate to APIM instance → APIs → [your API].
- Select All operations (or a specific operation).
- Open the Policy code editor.
- Paste the contents of
rdwr-azureapim-securepath-connector-v1.3.xml. - Click Save.
Via Azure CLI — az apim does NOT have a policy subgroup. Verify with az apim api -h: only operation, release, revision, schema, versionset exist. Upload via az rest against the canonical ARM REST API (works on core CLI; no extension required):
RG=<your-resource-group>
APIM=<your-apim-instance>
API_ID=<your-api-id> # the resource name of an APIM API in this instance,
# NOT the SecurePath endpoint hostname.
# List yours: az apim api list -g $RG --service-name $APIM --query "[].name" -o tsv
SUB=$(az account show --query id -o tsv)
URI="https://management.azure.com/subscriptions/$SUB/resourceGroups/$RG/providers/Microsoft.ApiManagement/service/$APIM/apis/$API_ID/policies/policy?api-version=2024-05-01"
# Wrap the XML file into the JSON envelope APIM's REST API expects.
# format MUST be "rawxml" — the default "xml" rejects {{named-value}} references inside XML attribute values.
jq -Rs '{properties: {format: "rawxml", value: .}}' \
rdwr-azureapim-securepath-connector-v1.3.xml \
> /tmp/apim-policy-body.json
az rest --method PUT --uri "$URI" \
--headers "Content-Type=application/json" \
--body @/tmp/apim-policy-body.jsonA successful response is the stored policy contract (HTTP 201 on first apply, 200 on update). If validation fails, APIM responds with 400 ValidationError and a per-line diagnostic (Line N, position M) — fix the XML and retry.
Via PowerShell (Windows / cross-platform):
$ctx = New-AzApiManagementContext -ResourceGroupName "<rg>" -ServiceName "<apim>"
Set-AzApiManagementPolicy -Context $ctx -ApiId "<api-id>" `
-PolicyFilePath ".\rdwr-azureapim-securepath-connector-v1.3.xml" `
-Format "application/vnd.ms-azure-apim.policy.raw+xml"The raw+xml format mirrors rawxml from the REST call — required for the same {{named-value}}-in-attribute reasons.
Via Bicep / ARM template (declarative / GitOps):
resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2024-05-01' = {
name: '${apimName}/${apiId}/policy'
properties: {
format: 'rawxml'
value: loadTextContent('./rdwr-azureapim-securepath-connector-v1.3.xml')
}
}# Send a benign test request
curl -v https://<apim-name>.azure-api.net/<api-path>/ \
-H "Ocp-Apim-Subscription-Key: <subscription-key>"Expected: HTTP 200 from the backend, indicating the request was inspected and allowed.
# Trigger a block — embed a typical SQL injection probe in the query string
curl -v "https://<apim-name>.azure-api.net/<api-path>/?id=1' OR '1'='1" \
-H "Ocp-Apim-Subscription-Key: <subscription-key>"Expected: HTTP 403 with the SecurePath block page, confirming the connector is enforcing verdicts.
To inspect the sideband flow in detail, enable APIM Tracing for one request and look at the send-request step — it will show the outgoing x-rdwr-* headers and the SecurePath verdict response.
If you need to remove the policy (e.g., during incident response or to revert to a prior version), there are two safe paths:
Path A — disable inspection without removing the policy. Set rdwr-app-ep-addr to an unreachable value or set rdwr-app-ep-timeout-seconds to 0. The policy fails open on connection error / timeout, so traffic flows to the backend without inspection. This is non-destructive and reversible by editing the Named Value back. Useful when you want to keep the policy in place for fast re-enablement after a SecurePath-side incident.
Path B — fully remove the policy. Replace the policy XML with the APIM default scope policy.
Via Azure Portal:
- Navigate to APIM instance → APIs → [your API] → All operations → Policy code editor.
- Replace the policy body with the default APIM scope policy:
<policies> <inbound><base /></inbound> <backend><base /></backend> <outbound><base /></outbound> <on-error><base /></on-error> </policies>
- Click Save. Inspection stops immediately on the next request.
Via Azure CLI — same az rest PUT used in Step 3, with the default scope policy as the body:
RG=<your-resource-group>
APIM=<your-apim-instance>
API_ID=<your-api-id>
SUB=$(az account show --query id -o tsv)
URI="https://management.azure.com/subscriptions/$SUB/resourceGroups/$RG/providers/Microsoft.ApiManagement/service/$APIM/apis/$API_ID/policies/policy?api-version=2024-05-01"
cat <<'EOF' > /tmp/default-scope-policy.xml
<policies>
<inbound><base /></inbound>
<backend><base /></backend>
<outbound><base /></outbound>
<on-error><base /></on-error>
</policies>
EOF
jq -Rs '{properties: {format: "rawxml", value: .}}' /tmp/default-scope-policy.xml \
> /tmp/default-scope-body.json
az rest --method PUT --uri "$URI" \
--headers "Content-Type=application/json" \
--body @/tmp/default-scope-body.jsonTo verify the rollback worked:
curl -v "https://<apim-name>.azure-api.net/<api-path>/?id=1' OR '1'='1" \
-H "Ocp-Apim-Subscription-Key: <subscription-key>"Expected: HTTP 200 (or whatever the backend returns) instead of HTTP 403 — the SQL-injection probe is no longer blocked, confirming the connector is no longer in the request path.
The Named Values created in Step 2 do not need to be deleted — they are inert without the policy. Leave them in place if you might re-enable inspection later, or az apim nv delete them if doing a permanent removal.
The policy decides how to forward request bodies based on Content-Length and content type:
| Condition | Behaviour |
|---|---|
Content-Length ≤ rdwr-body-max-size-bytes |
Full body forwarded |
Content-Length > rdwr-body-max-size-bytes |
Body not forwarded (Content-Length: 0 sent) |
| Chunked (no Content-Length) + matching content type | Body forwarded up to rdwr-partial-body-size-bytes |
| Chunked + non-matching content type | No body forwarded |
multipart/form-data |
Body forwarded up to rdwr-multipart-max-size-bytes |
| Body truncated | x-rdwr-partial-body: true header added |
APIM prepends a base path (e.g., /api) to all operations. SecurePath expects the original URI without this prefix. Set rdwr-api-base-path to your API's base path (e.g., /api) and the policy strips it from the sideband URI.
When static-extensions-enabled is true, requests are skipped if all three conditions are met:
- The HTTP method is in
static-list-of-methods-not-to-inspect(e.g.,GET,HEAD). - The URI ends with an extension in
static-list-of-bypassed-extensions(e.g.,.png,.css). - No query string is present (unless
static-inspect-if-query-string-existsisfalse).
When rdwr-bot-manager-enabled is true:
- BM cookies from SecurePath responses (
__uzm*,uzmcr) are propagated to the client viaSet-Cookieheaders. ShieldSquare-Responseis forwarded.- The
uzmcrheader overrides block verdicts to support mobile redirect handling. - BM cookies propagate on all verdict paths (allow, block, redirect).
The response-phase log fires automatically whenever SecurePath returns:
x-rdwr-oop-id— correlation UUIDx-rdwr-oop-log— log mode (2= headers only,3= headers + body)x-rdwr-oop-log-body— body capture limit (e.g.,2k)
The log is sent via send-one-way-request (fire-and-forget) in the outbound pipeline.
Captured response metadata includes:
x-rdwr-o2v-status— origin response status codex-rdwr-o2v-bytes-sent— total wire bytes (status line + headers + body, matching NGINX$bytes_sentsemantics; v1.3.2 fix)x-rdwr-o2v-body-bytes-sent— body bytes only (matching NGINX$body_bytes_sent)x-rdwr-o2h-content-type— origin Content-Type headerx-rdwr-o2h-rdwr-response— connector disposition (allowed/blocked)- Base64-encoded response body sample (mode 3 only, configurable size)
- ✅ Store
rdwr-api-keyas a secret Named Value (--secret truein the CLI). Secret Named Values are masked in policy exports and traces. - ✅ Upload both root and intermediate CA certificates before enabling the policy. The intermediate alone will fail the TLS handshake.
- ✅ Test in a non-production API first. Apply the policy to a staging API, validate allow/block flows, then promote to production.
- ✅ Set
rdwr-app-ep-timeout-secondsto a value you can tolerate as worst-case latency. The default of10sis safe; reduce it for latency-sensitive APIs. - ✅ Set
rdwr-api-base-pathto match your APIM API's base path so SecurePath sees the correct URI. - ✅ Use APIM Tracing for one-off troubleshooting. The trace shows the full sideband request and SecurePath response.
- ✅ Monitor APIM
BackendDurationand policy execution time in Application Insights or Azure Monitor — sideband adds inspection latency proportional to network distance to the SecurePath region. - ✅ Update
plugin-version-infowhen you upgrade the policy XML so the SecurePath portal reports the correct connector version.
- ❌ Don't commit the API key to source control — even in IaC files. Use Azure Key Vault references or pipeline secrets.
- ❌ Don't apply this policy at the global / All-APIs level unless you intend every API to be inspected. Apply at the API or operation level for finer control.
- ❌ Don't combine this policy with another policy that modifies request bodies before the sideband call. Body modifications upstream will be sent to SecurePath, which may produce unexpected verdicts.
- ❌ Don't disable
ignore-erroron the sidebandsend-request. It guarantees fail-open behaviour. Disabling it makes SecurePath unavailability customer-impacting. - ❌ Don't allow client-supplied
x-rdwr-*headers to reach this policy untouched from upstream proxies. The reserved-header check returns 403 by design — this is a security feature, not a bug. - ❌ Don't reduce
rdwr-app-ep-timeout-secondsbelow2unless you have measured your typical SecurePath round-trip latency. Aggressive timeouts cause spurious fail-open traffic. - ❌ Don't skip the CA certificate upload step. TLS handshake failures will fail the sideband call and trigger fail-open on every request.
- ❌ Don't edit the
<choose>verdict logic in the XML unless you understand the SecurePath verdict semantics. Verdict misclassification can cause bypass of legitimate blocks.
The policy sends the following headers in every sideband request to SecurePath:
| Header | Description |
|---|---|
x-rdwr-app-id |
Application identifier |
x-rdwr-api-key |
API key for authentication |
x-rdwr-connector-ip |
Client IP, taken from rdwr-true-client-ip-header if present |
x-rdwr-connector-port |
Always 443 (Azure APIM ingress) |
x-rdwr-connector-scheme |
Always https (Azure forces HTTPS termination) |
x-rdwr-host |
Original Host header from the client request |
x-rdwr-plugin-info |
Connector platform and version (default 700-v1.3.2) |
x-rdwr-partial-body |
true when the body was truncated due to size limits |
x-rdwr-connector-proto |
2 in the response-phase log |
x-rdwr-connector-stage |
request (sideband) or log (response-phase) |
x-rdwr-o2h-rdwr-response |
allowed or blocked — connector disposition (response-phase log only) |
The policy returns HTTP 403 for any incoming client request containing these headers (because they would otherwise allow a client to spoof inspection metadata):
x-rdwr-app-id,x-rdwr-api-key,x-rdwr-connector-ip,x-rdwr-partial-body,x-rdwr-cdn-ip,x-rdwr-ip
If the sideband request to SecurePath times out or fails, the policy allows the request to proceed to the backend. Set a strict rdwr-app-ep-timeout-seconds to control worst-case latency exposure.
Always store rdwr-api-key as a secret Named Value. Secret Named Values are masked in policy exports, traces, and the Azure Portal UI.
| Constraint | Impact | Mitigation |
|---|---|---|
| No response body modification | JavaScript injection verdicts cannot be enforced | Minimal — APIM serves API traffic (JSON/XML), not browser-rendered HTML |
x-rdwr-connector-scheme is always https |
Cosmetic only — Azure forces HTTPS | None needed |
| No dedicated path proxying within a single XML policy | BM browser-fingerprinting paths cannot be served from the policy | Configure /18f5.../ and /c99a.../ as separate API operations pointing to SP |
send-one-way-request returns no response |
Response-phase log success cannot be checked from policy | Monitor SecurePath portal for log ingestion correlation |
| APIM expression sandbox limits .NET APIs | Some advanced parsing (e.g., regex) is unavailable | None — the policy is written within sandbox limits |
- Check whether the client (or an upstream proxy) is sending
x-rdwr-*headers — the policy returns 403 by design when these are present. - Verify the
rdwr-app-idandrdwr-api-keyNamed Values match the values shown in the Radware Cloud portal. - Enable APIM Tracing for one request and inspect the SecurePath sideband response body for the verdict reason.
- Confirm both
rdwr-root-ca.pemandrdwr-intermediate-ca.pemare uploaded to the APIM CA store. - If your network uses a TLS-inspecting proxy (e.g., Zscaler), upload that proxy's root CA to the APIM CA store as well.
- Verify outbound port 443 connectivity from the APIM VNet to
*.oop.radwarecloud.net.
- Increase
rdwr-app-ep-timeout-seconds(default10). - Verify there are no NSG, firewall, or routing rules blocking egress from APIM to SecurePath.
- Check the SecurePath status page for service availability in your region.
- Named Values use double-brace syntax:
{{rdwr-app-id}}. - Confirm each Named Value exists and is not disabled.
- Secret Named Values are masked in the policy editor but resolve correctly at runtime.
- Check
rdwr-body-max-size-bytes— bodies larger than this are skipped (this is intentional). - For chunked requests, ensure the
Content-Typeis inchunked-request-allowed-content-types. - Set
rdwr-api-base-pathcorrectly so SecurePath sees the right URI path.
- Confirm
rdwr-bot-manager-enabledand the response-logging Named Values are configured. - Verify SecurePath returned
x-rdwr-oop-idin the sideband response — without it, the log is not emitted. send-one-way-requestis fire-and-forget; failures are not visible in APIM. Use SecurePath portal correlation as the source of truth.
| You want to | Read |
|---|---|
| Get something running | The Quickstart at the top |
| See every config knob | Configuration Reference |
| Understand tier / VNet / Consumption / self-hosted differences | Deployment shapes |
| Roll back or disable the connector | Step 5 — Uninstall / rollback |
| Debug something that's not working | Troubleshooting |
| See what changed between versions | release-notes.md |
For SecurePath-account questions (provisioning, billing, application configuration on the SecurePath side) contact your Radware account team. For connector bugs and feature requests, file an issue on this GitHub repo.
| Version | Date | Status | Highlights |
|---|---|---|---|
| v1.3.2 | 2026-05-03 | Current | x-rdwr-o2v-bytes-sent reports total wire bytes (status line + headers + body) |
| v1.3.1 | 2026-03-31 | Superseded | Disposition header, v2 log on block/redirect, body-truncation fix |
| v1.3.0 | 2026-03-12 | Superseded | GA release, full SecurePath feature coverage, response-phase logging |
| v1.2.0 | 2025-11-30 | Superseded | Bot Manager support, reserved header enforcement |
| v1.1.0 | 2025-09-15 | Superseded | Body handling, chunked request support |
| v1.0.0 | 2025-05-01 | Superseded | Initial release |
For full release notes, see release-notes.md.
Copyright © 2024–2026 Radware Ltd. All rights reserved.
Proprietary and confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.