Files
haproxy-manager-base/errors/403-waf.html

110 lines
3.5 KiB
HTML
Raw Normal View History

waf-block: render a real HTML page on Coraza-denied requests Previously a Coraza block returned an empty 403 with only the `waf-block: request` header — a legitimate site owner caught in a false-positive had no idea what happened or how to get help. Now: - hap_header.tpl: every request gets a unique-id (uuid()) and that ID is injected back into the request as X-Request-Reference for the backend, so upstream Apache/PHP logs can correlate too. - hap_listener.tpl: on a request-phase Coraza deny we use `http-request return` with `lf-file` instead of `http-request deny`, so HAProxy renders the new errors/403-waf.html page with the request reference substituted in. The page tells the visitor a request was blocked, displays the reference, and points site owners to https://secure.anhonesthost.com/submitticket.php to open a ticket rather than exposing a public email address (avoids giving attackers a flood target). - The waf-block header and x-request-reference header are still set on the response so curl / monitoring clients can pick them up without rendering HTML. - Response-phase deny stays as the bare 403 — outbound blocks are rare in our config and an HTML body could land mid-stream. Errorfile lives at /haproxy/errors/403-waf.html (NOT under /etc/haproxy/, because that path is a named volume in deployed containers and would shadow baked-in files on existing deployments). Support workflow: visitor quotes the reference → support greps /var/log/haproxy.log for the uuid → gets timestamp + client IP + Host + URI → greps /var/log/coraza/audit.log for the matching transaction → reads the rule_id that fired. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 05:41:16 -07:00
<!DOCTYPE html>
<!--
Served by HAProxy via `lf-file` on Coraza WAF deny.
IMPORTANT: HAProxy's lf-file expansion treats `%` as the start of a
log-format expression. Literal percent signs (CSS 100%, gradient stops,
url-encoded data, etc.) MUST be doubled as `%%` or HAProxy will silently
swallow them. Expressions like `%[unique-id]` / `%[req.hdr(host)]` stay
single-`%` — those are the substitutions we want.
-->
waf-block: render a real HTML page on Coraza-denied requests Previously a Coraza block returned an empty 403 with only the `waf-block: request` header — a legitimate site owner caught in a false-positive had no idea what happened or how to get help. Now: - hap_header.tpl: every request gets a unique-id (uuid()) and that ID is injected back into the request as X-Request-Reference for the backend, so upstream Apache/PHP logs can correlate too. - hap_listener.tpl: on a request-phase Coraza deny we use `http-request return` with `lf-file` instead of `http-request deny`, so HAProxy renders the new errors/403-waf.html page with the request reference substituted in. The page tells the visitor a request was blocked, displays the reference, and points site owners to https://secure.anhonesthost.com/submitticket.php to open a ticket rather than exposing a public email address (avoids giving attackers a flood target). - The waf-block header and x-request-reference header are still set on the response so curl / monitoring clients can pick them up without rendering HTML. - Response-phase deny stays as the bare 403 — outbound blocks are rare in our config and an HTML body could land mid-stream. Errorfile lives at /haproxy/errors/403-waf.html (NOT under /etc/haproxy/, because that path is a named volume in deployed containers and would shadow baked-in files on existing deployments). Support workflow: visitor quotes the reference → support greps /var/log/haproxy.log for the uuid → gets timestamp + client IP + Host + URI → greps /var/log/coraza/audit.log for the matching transaction → reads the rule_id that fired. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 05:41:16 -07:00
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>Request blocked &middot; %[req.hdr(host)]</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%%; }
waf-block: render a real HTML page on Coraza-denied requests Previously a Coraza block returned an empty 403 with only the `waf-block: request` header — a legitimate site owner caught in a false-positive had no idea what happened or how to get help. Now: - hap_header.tpl: every request gets a unique-id (uuid()) and that ID is injected back into the request as X-Request-Reference for the backend, so upstream Apache/PHP logs can correlate too. - hap_listener.tpl: on a request-phase Coraza deny we use `http-request return` with `lf-file` instead of `http-request deny`, so HAProxy renders the new errors/403-waf.html page with the request reference substituted in. The page tells the visitor a request was blocked, displays the reference, and points site owners to https://secure.anhonesthost.com/submitticket.php to open a ticket rather than exposing a public email address (avoids giving attackers a flood target). - The waf-block header and x-request-reference header are still set on the response so curl / monitoring clients can pick them up without rendering HTML. - Response-phase deny stays as the bare 403 — outbound blocks are rare in our config and an HTML body could land mid-stream. Errorfile lives at /haproxy/errors/403-waf.html (NOT under /etc/haproxy/, because that path is a named volume in deployed containers and would shadow baked-in files on existing deployments). Support workflow: visitor quotes the reference → support greps /var/log/haproxy.log for the uuid → gets timestamp + client IP + Host + URI → greps /var/log/coraza/audit.log for the matching transaction → reads the rule_id that fired. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 05:41:16 -07:00
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
color: #1f2937;
background: linear-gradient(135deg, #f9fafb 0%%, #eef2f7 100%%);
waf-block: render a real HTML page on Coraza-denied requests Previously a Coraza block returned an empty 403 with only the `waf-block: request` header — a legitimate site owner caught in a false-positive had no idea what happened or how to get help. Now: - hap_header.tpl: every request gets a unique-id (uuid()) and that ID is injected back into the request as X-Request-Reference for the backend, so upstream Apache/PHP logs can correlate too. - hap_listener.tpl: on a request-phase Coraza deny we use `http-request return` with `lf-file` instead of `http-request deny`, so HAProxy renders the new errors/403-waf.html page with the request reference substituted in. The page tells the visitor a request was blocked, displays the reference, and points site owners to https://secure.anhonesthost.com/submitticket.php to open a ticket rather than exposing a public email address (avoids giving attackers a flood target). - The waf-block header and x-request-reference header are still set on the response so curl / monitoring clients can pick them up without rendering HTML. - Response-phase deny stays as the bare 403 — outbound blocks are rare in our config and an HTML body could land mid-stream. Errorfile lives at /haproxy/errors/403-waf.html (NOT under /etc/haproxy/, because that path is a named volume in deployed containers and would shadow baked-in files on existing deployments). Support workflow: visitor quotes the reference → support greps /var/log/haproxy.log for the uuid → gets timestamp + client IP + Host + URI → greps /var/log/coraza/audit.log for the matching transaction → reads the rule_id that fired. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 05:41:16 -07:00
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
line-height: 1.5;
}
.card {
background: #fff;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 12px 32px rgba(31,41,55,0.08);
max-width: 560px;
width: 100%%;
waf-block: render a real HTML page on Coraza-denied requests Previously a Coraza block returned an empty 403 with only the `waf-block: request` header — a legitimate site owner caught in a false-positive had no idea what happened or how to get help. Now: - hap_header.tpl: every request gets a unique-id (uuid()) and that ID is injected back into the request as X-Request-Reference for the backend, so upstream Apache/PHP logs can correlate too. - hap_listener.tpl: on a request-phase Coraza deny we use `http-request return` with `lf-file` instead of `http-request deny`, so HAProxy renders the new errors/403-waf.html page with the request reference substituted in. The page tells the visitor a request was blocked, displays the reference, and points site owners to https://secure.anhonesthost.com/submitticket.php to open a ticket rather than exposing a public email address (avoids giving attackers a flood target). - The waf-block header and x-request-reference header are still set on the response so curl / monitoring clients can pick them up without rendering HTML. - Response-phase deny stays as the bare 403 — outbound blocks are rare in our config and an HTML body could land mid-stream. Errorfile lives at /haproxy/errors/403-waf.html (NOT under /etc/haproxy/, because that path is a named volume in deployed containers and would shadow baked-in files on existing deployments). Support workflow: visitor quotes the reference → support greps /var/log/haproxy.log for the uuid → gets timestamp + client IP + Host + URI → greps /var/log/coraza/audit.log for the matching transaction → reads the rule_id that fired. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 05:41:16 -07:00
padding: 36px 40px;
}
.badge {
display: inline-block;
background: #fef3c7;
color: #92400e;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 999px;
margin-bottom: 16px;
}
h1 { font-size: 22px; margin: 0 0 12px; color: #111827; }
p { margin: 0 0 14px; color: #374151; }
.ref {
background: #f3f4f6;
border: 1px solid #e5e7eb;
border-radius: 6px;
padding: 12px 14px;
margin: 20px 0;
font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
font-size: 13px;
color: #111827;
word-break: break-all;
}
.ref-label {
display: block;
color: #6b7280;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
margin-bottom: 4px;
font-family: inherit;
}
.owner {
border-top: 1px solid #e5e7eb;
margin-top: 24px;
padding-top: 20px;
color: #4b5563;
font-size: 14px;
}
.owner h2 { font-size: 14px; font-weight: 600; color: #111827; margin: 0 0 8px; }
a {
color: #1d4ed8;
text-decoration: none;
border-bottom: 1px solid transparent;
}
a:hover, a:focus { border-bottom-color: #1d4ed8; }
.small { font-size: 12px; color: #6b7280; margin-top: 16px; }
</style>
</head>
<body>
<main class="card" role="main">
<span class="badge">Access blocked</span>
<h1>Your request was blocked by our security filter</h1>
<p>The request to <strong>%[req.hdr(host)]</strong> looked suspicious to our web application firewall and was not delivered to the site.</p>
<p>This is automated. No one has reviewed the request yet.</p>
<div class="ref">
<span class="ref-label">Request reference</span>
%[unique-id]
</div>
<div class="owner">
<h2>Site owner?</h2>
<p>If you operate this site and believe this block is incorrect, please contact your hosting provider's support team and include the request reference above. They can look up exactly which rule fired and adjust it if it's a false positive.</p>
waf-block: render a real HTML page on Coraza-denied requests Previously a Coraza block returned an empty 403 with only the `waf-block: request` header — a legitimate site owner caught in a false-positive had no idea what happened or how to get help. Now: - hap_header.tpl: every request gets a unique-id (uuid()) and that ID is injected back into the request as X-Request-Reference for the backend, so upstream Apache/PHP logs can correlate too. - hap_listener.tpl: on a request-phase Coraza deny we use `http-request return` with `lf-file` instead of `http-request deny`, so HAProxy renders the new errors/403-waf.html page with the request reference substituted in. The page tells the visitor a request was blocked, displays the reference, and points site owners to https://secure.anhonesthost.com/submitticket.php to open a ticket rather than exposing a public email address (avoids giving attackers a flood target). - The waf-block header and x-request-reference header are still set on the response so curl / monitoring clients can pick them up without rendering HTML. - Response-phase deny stays as the bare 403 — outbound blocks are rare in our config and an HTML body could land mid-stream. Errorfile lives at /haproxy/errors/403-waf.html (NOT under /etc/haproxy/, because that path is a named volume in deployed containers and would shadow baked-in files on existing deployments). Support workflow: visitor quotes the reference → support greps /var/log/haproxy.log for the uuid → gets timestamp + client IP + Host + URI → greps /var/log/coraza/audit.log for the matching transaction → reads the rule_id that fired. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 05:41:16 -07:00
</div>
<p class="small">Reference IDs expire from our active logs after 14 days, so please open a ticket promptly if you'd like this investigated.</p>
</main>
</body>
</html>