Configure OCI Regional WAF Rate Limiting with a Custom 503 Response Using OCI CLI
Overview
This guide shows how to configure an Oracle Cloud Infrastructure regional Web Application Firewall policy to:
- Monitor requests to
eits-uat.fcicanada.com. - Apply rate limiting only to requests whose path begins with
/apex/. - Limit each client IP address to 100 requests within 60 seconds.
- Return a custom HTTP 503 page for 300 seconds when the threshold is exceeded.
- Preserve all existing WAF actions and rules.
The tested regional WAF policy was:
Policy name: QA_RegionalWAF_Policy
Region: ca-toronto-1
Lifecycle state: ACTIVE
Target host: eits-uat.fcicanada.com
Target path: /apex/
Prerequisites
- OCI CLI installed and configured.
- An OCI CLI profile with permission to read and update the WAF policy.
jqinstalled.- The regional WAF policy OCID.
- A Linux shell such as Bash.
Step 1: Create a Working Directory
mkdir -p rate_limiting_waf
cd rate_limiting_waf
Step 2: Set Environment Variables
Set the OCI profile, region, WAF policy OCID, action name, and rule name.
export OCI_PROFILE="CLOUD_ADMIN_FCIAS2"
export OCI_REGION="ca-toronto-1"
export WAF_POLICY_ID="ocid1.webappfirewallpolicy.oc1.ca-toronto-1.REPLACE_WITH_POLICY_OCID"
export ACTION_NAME="EITS-UAT-HIGH-VOLUME-503"
export RULE_NAME="EITS-UAT-APEX-RATE-LIMIT"
Step 3: Verify the WAF Policy
oci waf web-app-firewall-policy get \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
--query 'data.{"Name":"display-name","State":"lifecycle-state","OCID":id}' \
--output table
Example output:
+-----------------------+----------------------------------------------+--------+
| Name | OCID | State |
+-----------------------+----------------------------------------------+--------+
| QA_RegionalWAF_Policy | ocid1.webappfirewallpolicy... | ACTIVE |
+-----------------------+----------------------------------------------+--------+
Confirm that the policy is the correct one and that its lifecycle state is ACTIVE.
Step 4: Back Up the Existing WAF Policy
The OCI CLI update command submits complete policy sections. Back up the policy before changing the actions or request-rate-limiting arrays.
oci waf web-app-firewall-policy get \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
> waf-policy-before-rate-limit.json
Extract the existing actions and rate-limiting configuration:
jq '.data.actions' \
waf-policy-before-rate-limit.json \
> actions-before.json
jq '.data."request-rate-limiting"' \
waf-policy-before-rate-limit.json \
> request-rate-limiting-before.json
In this implementation, the original rate-limiting section returned:
null
That confirmed that no request rate-limiting rules were configured before this change.
Step 5: Create the Custom 503 HTML Page
Create the static response page that OCI WAF will return when a client exceeds the configured request threshold.
cat > eits-high-volume.html <<'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EITS Intake – Temporarily Unavailable</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #f5f6f7;
color: #222;
font-family: Arial, Helvetica, sans-serif;
}
.message {
max-width: 700px;
margin: 20px;
padding: 40px;
text-align: center;
background: #fff;
border-radius: 8px;
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.12);
}
h1 {
margin-top: 0;
font-size: 28px;
}
p {
font-size: 18px;
line-height: 1.6;
}
</style>
</head>
<body>
<main class="message">
<h1>503 Service Temporarily Unavailable</h1>
<p>
EITS Intake is currently experiencing a high volume of requests
and is unable to process your request at this time.
</p>
<p>
Please wait a few minutes and try again. We apologize for the
inconvenience and appreciate your patience.
</p>
</main>
</body>
</html>
EOF
<h1>503 Service Temporarily Unavailable /h1>. The version above corrects it to a valid closing tag: </h1>.
Step 6: Build the Complete WAF Actions JSON
The following command reads the current action list, removes any older action with the same name, and appends the custom RETURN_HTTP_RESPONSE action.
jq \
--rawfile html eits-high-volume.html \
--arg actionName "$ACTION_NAME" \
'
(.data.actions // [])
| map(select(.name != $actionName))
+ [
{
"name": $actionName,
"type": "RETURN_HTTP_RESPONSE",
"code": 503,
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
},
{
"name": "Retry-After",
"value": "300"
},
{
"name": "Cache-Control",
"value": "no-store"
}
],
"body": {
"type": "STATIC_TEXT",
"text": $html
}
}
]
' waf-policy-before-rate-limit.json > actions.json
Review the generated JSON:
jq . actions.json
The new action should include:
{
"name": "EITS-UAT-HIGH-VOLUME-503",
"type": "RETURN_HTTP_RESPONSE",
"code": 503,
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
},
{
"name": "Retry-After",
"value": "300"
},
{
"name": "Cache-Control",
"value": "no-store"
}
],
"body": {
"type": "STATIC_TEXT",
"text": "..."
}
}
Step 7: Add the Custom Action to the WAF Policy
oci waf web-app-firewall-policy update \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--actions file://actions.json \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
--wait-for-state SUCCEEDED \
--force
Successful output includes:
"operation-type": "UPDATE_WAF_POLICY"
"percent-complete": 100.0
"status": "SUCCEEDED"
Step 8: Verify the Custom Action
oci waf web-app-firewall-policy get \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
--query "data.actions[?name=='${ACTION_NAME}']" \
--output json
Confirm that the returned action has:
type:RETURN_HTTP_RESPONSEcode:503Content-Type:text/html; charset=UTF-8Retry-After:300Cache-Control:no-store
Step 9: Retrieve the Policy Again
Retrieve the policy after the action has been added. This refreshed JSON will be used to preserve the current configuration while adding the rate-limiting rule.
oci waf web-app-firewall-policy get \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
> waf-policy-with-action.json
Step 10: Build the Rate-Limiting Rule
The rule below applies only when both of these conditions are true:
- The HTTP host is
eits-uat.fcicanada.com. - The request path begins with
/apex/.
It does not match a specific APEX session identifier because values such as 10395221140358 change between sessions.
jq \
--arg ruleName "$RULE_NAME" \
--arg actionName "$ACTION_NAME" \
'
{
"rules":
(
(.data."request-rate-limiting".rules // [])
| map(select(.name != $ruleName))
+ [
{
"name": $ruleName,
"type": "REQUEST_RATE_LIMITING",
"conditionLanguage": "JMESPATH",
"condition": "http.request.host == '\''eits-uat.fcicanada.com'\'' && starts_with(http.request.url.path, '\''/apex/'\'')",
"actionName": $actionName,
"configurations": [
{
"requestsLimit": 100,
"periodInSeconds": 60,
"actionDurationInSeconds": 300
}
]
}
]
)
}
' waf-policy-with-action.json > request-rate-limiting.json
Review the generated rate-limiting configuration:
jq . request-rate-limiting.json
Expected output:
{
"rules": [
{
"name": "EITS-UAT-APEX-RATE-LIMIT",
"type": "REQUEST_RATE_LIMITING",
"conditionLanguage": "JMESPATH",
"condition": "http.request.host == 'eits-uat.fcicanada.com' && starts_with(http.request.url.path, '/apex/')",
"actionName": "EITS-UAT-HIGH-VOLUME-503",
"configurations": [
{
"requestsLimit": 100,
"periodInSeconds": 60,
"actionDurationInSeconds": 300
}
]
}
]
}
Step 11: Apply the Rate-Limiting Rule
oci waf web-app-firewall-policy update \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--request-rate-limiting file://request-rate-limiting.json \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
--wait-for-state SUCCEEDED \
--force
Successful output includes:
"operation-type": "UPDATE_WAF_POLICY"
"percent-complete": 100.0
"status": "SUCCEEDED"
Step 12: Verify the Rate-Limiting Rule
oci waf web-app-firewall-policy get \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
--query "data.\"request-rate-limiting\".rules[?name=='${RULE_NAME}']" \
--output json
Verified output:
[
{
"action-name": "EITS-UAT-HIGH-VOLUME-503",
"condition": "http.request.host == 'eits-uat.fcicanada.com' && starts_with(http.request.url.path, '/apex/')",
"condition-language": "JMESPATH",
"configurations": [
{
"action-duration-in-seconds": 300,
"period-in-seconds": 60,
"requests-limit": 100
}
],
"name": "EITS-UAT-APEX-RATE-LIMIT",
"type": "REQUEST_RATE_LIMITING"
}
]
Step 13: List All WAF Actions
oci waf web-app-firewall-policy get \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
--query 'data.actions[].{Name:name,Type:type}' \
--output table
Verified output:
+-----------------------------------------+----------------------+
| Name | Type |
+-----------------------------------------+----------------------+
| Pre-configured Check Action | CHECK |
| Pre-configured Allow Action | ALLOW |
| Pre-configured 401 Response Code Action | RETURN_HTTP_RESPONSE |
| Unauthorized to access this web site | RETURN_HTTP_RESPONSE |
| maintenance-503 | RETURN_HTTP_RESPONSE |
| EITS-UAT-HIGH-VOLUME-503 | RETURN_HTTP_RESPONSE |
+-----------------------------------------+----------------------+
How the Configuration Works
Client request
|
v
OCI Regional WAF
|
|-- Host is not eits-uat.fcicanada.com
| '-- Rate-limit rule does not apply
|
|-- Path does not begin with /apex/
| '-- Rate-limit rule does not apply
|
'-- Host and path match
|
|-- Up to 100 requests within 60 seconds
| '-- Request continues to the reverse proxy
|
'-- Threshold exceeded for that client IP
'-- Return custom HTTP 503 page for 300 seconds
Configuration Values
| Setting | Value | Meaning |
|---|---|---|
requestsLimit |
100 | Maximum requests allowed from one client IP during the evaluation period. |
periodInSeconds |
60 | The request-counting window. |
actionDurationInSeconds |
300 | How long the custom response action remains active for the client IP after the threshold is exceeded. |
Retry-After |
300 | Advises the client to retry after five minutes. |
Testing
Use a non-production client and a safe URL. Avoid sending repeated requests to an APEX form submission endpoint.
seq 1 150 | xargs -n1 -P20 \
curl -sk -o /dev/null \
-w '%{http_code}\n' \
'https://eits-uat.fcicanada.com/apex/'
Expected behavior:
- Initial requests receive the normal application response.
- After the client IP exceeds the threshold, responses change to HTTP 503.
- The custom HTML message is displayed.
- The client IP remains subject to the action for 300 seconds.
To inspect response headers:
curl -skI https://eits-uat.fcicanada.com/apex/
During rate limiting, the response should include:
HTTP/1.1 503 Service Unavailable
Content-Type: text/html; charset=UTF-8
Retry-After: 300
Cache-Control: no-store
Rollback
Restore the Previous Actions
oci waf web-app-firewall-policy update \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--actions file://actions-before.json \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
--wait-for-state SUCCEEDED \
--force
Remove the Rate-Limiting Rule
Because the original rate-limiting configuration was null, use an empty rule list to remove the rule:
cat > request-rate-limiting-empty.json <<'EOF'
{
"rules": []
}
EOF
oci waf web-app-firewall-policy update \
--web-app-firewall-policy-id "$WAF_POLICY_ID" \
--request-rate-limiting file://request-rate-limiting-empty.json \
--profile "$OCI_PROFILE" \
--region "$OCI_REGION" \
--wait-for-state SUCCEEDED \
--force
Operational Considerations
- Shared public IP addresses: Users behind Skyhigh, corporate NAT, VPN, or proxy infrastructure may appear as one client IP. Choose the request threshold using observed production traffic.
- APEX sessions: Do not include the changing APEX session number in the WAF condition.
- HTTP status: HTTP 503 is appropriate because the restriction represents temporary unavailability. HTTP 502 should normally be reserved for an invalid upstream gateway response.
- Policy replacement behavior: The CLI update sends the entire actions or rate-limiting section. Always merge with the existing policy instead of submitting only the new object.
- Monitoring: Review WAF logs after activation to confirm the rule is not affecting legitimate users.
Final Result
The regional WAF policy now protects the EITS UAT APEX endpoint with a per-client-IP request limit. A client that sends more than 100 matching requests within 60 seconds receives a custom HTTP 503 response for 300 seconds. Existing WAF actions remain preserved.

No comments:
Post a Comment