Example mitigation rules
A customer support chatbot should not engage with prompts about violent crimes or hate speech. This custom rule blocks the request and returns a JSON response that your application can parse and display to the user.
-
When incoming requests match:
Field Operator Value LLM Unsafe topic categories is in S1: Violent CrimesS10: HateExpression when using the editor:
(any(cf.llm.prompt.unsafe_topic_categories[*] in {"S1" "S10"})) -
Action: Block
-
With response type: Custom JSON
-
Response body:
{ "error": "content_policy", "message": "Your message could not be processed because it touches on a topic outside this assistant's scope. Please rephrase your question." }
Your application can check for a non-200 response and display the message field to the user, keeping the experience conversational instead of showing a raw block page.
This rule combines AI Security for Apps's injection score with Bot Management and the request's country to focus on high-confidence attacks from automated sources. This layered approach significantly reduces false positives compared to using any single signal alone.
-
When incoming requests match:
Enter the following expression in the editor:
(cf.llm.prompt.injection_score lt 25 and cf.bot_management.score lt 10 and ip.geoip.country ne "US") -
Action: Block
The rule targets requests that are simultaneously:
- Likely prompt injection attempts (score below 25).
- Coming from automated tooling, not a real browser (bot score below 10).
- Originating from outside the US — adjust the country code to match where your users are.
Any single signal might produce false positives on its own. Together, they identify a pattern strongly associated with automated prompt injection attacks.
A financial services application legitimately handles credit card and bank account numbers from internal agents, but should block those PII types from external users. This rule uses the request's autonomous system number (ASN) to distinguish internal traffic from public traffic.
-
When incoming requests match:
Enter the following expression in the editor:
(any(cf.llm.prompt.pii_categories[*] in {"CREDIT_CARD" "US_BANK_NUMBER" "IBAN_CODE"}) and ip.src.asnum ne 13335)Replace
13335with your organization's ASN. -
Action: Block
-
With response type: Custom JSON
-
Response body:
{ "error": "pii_blocked", "message": "Financial account information cannot be submitted from external networks. If you are an internal agent, connect to the corporate network and try again." }
Internal agents on your corporate network (identified by ASN) can submit financial PII to the AI assistant as part of their workflow, while external users are blocked. You could further refine this by combining with Access service tokens or mTLS for stronger identity verification.
When a WAF rule blocks a request, Cloudflare sends the block response back to your application — not to the end user. Your application needs to handle that response and decide what to show. Without error handling, your users may see a raw HTML error page or a broken UI.
Here are two things you can do to keep the experience smooth.
Define a friendly default message that your application displays whenever it receives a non-successful response. This works regardless of how the block rule is configured — including the default Cloudflare block page, which returns HTML that would otherwise break a JSON-based chat UI.
// Define a user-friendly fallback message. This is what the user will see// any time the request is blocked or something unexpected happens.const FALLBACK = "Sorry, I can't process that request. Please try rephrasing.";
const resp = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: userMessage }),});
// If the response is not 2xx, show the fallback instead of trying to parse// the body. This safely handles the default Cloudflare block page (which is// HTML) without breaking your UI.if (!resp.ok) { await resp.text(); // consume the body so the connection is released showError(FALLBACK); return;}
const data = await resp.json();showMessage(data.message);For more control, configure your block rules with a custom JSON response — for example, { "message": "That question is outside this assistant's scope." }. Your application can then parse the response and show the custom message when available, falling back to the default when it is not.
const FALLBACK = "Sorry, I can't process that request. Please try rephrasing.";
const resp = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: userMessage }),});
if (!resp.ok) { // Check the content type to determine if the response contains a custom // JSON error from your WAF rule, or something else (like the default // Cloudflare HTML block page, or a DDoS / Bot Management challenge). const ct = (resp.headers.get("content-type") || "").toLowerCase();
if (ct.includes("application/json")) { // The WAF returned your custom JSON response. Parse it and show the // message you configured in the rule. Fall back to the default if the // field is missing or empty. const data = await resp.json(); showError(data.message || FALLBACK); } else { // The response is not JSON — most likely the default Cloudflare HTML // block page. Discard the body and show the friendly fallback. await resp.text(); showError(FALLBACK); } return;}
const data = await resp.json();showMessage(data.message);