Quick answer
A web application firewall (WAF) is a security layer that sits in front of a website or API and inspects every HTTP request before it reaches the application. It looks at the request itself — the path, headers, query string, body and the client behind them — and blocks patterns that indicate an attack, such as SQL injection, cross-site scripting or credential stuffing. Unlike a network firewall, which decides based on IP addresses and ports, a WAF understands the application protocol and can therefore stop attacks that arrive over ports you have deliberately left open.
01How a WAF works
A WAF is a reverse proxy: traffic is addressed to it rather than to your server, it decides what to do with each request, and it forwards only what passes. Because it terminates the connection itself, it sees the fully assembled, decrypted request, including headers and bodies a network device would never parse.
For each request the WAF evaluates a set of rules. Some are signature-based: a pattern such as ' OR 1=1-- in a query parameter is a well-known SQL injection probe. Others are behavioural: a client that requests hundreds of distinct URLs within a minute, or one that claims to be a browser while behaving like a script. The outcome is an action — allow, challenge, rate limit, log or block.
Many WAFs score rather than switch: several weak signals add up to a risk score, and the action depends on where the total lands. The OWASP Core Rule Set (CRS), a widely used open-source ruleset, is the textbook example. Each matching rule adds points by severity — 5 for critical, 4 for error, 3 for warning, 2 for notice — and the request is blocked once the total reaches the inbound anomaly threshold, 5 by default. Any single signal has false positives; several independent signals agreeing rarely do.
02What a WAF blocks
Most WAFs start from a managed ruleset that covers generic attack classes without you writing anything:
- SQL injection — manipulating database queries through user input
- Cross-site scripting (XSS) — injecting scripts that run in another visitor's browser
- Path traversal — using ../ sequences to read files outside the web root
- Remote code execution probes — exploit strings aimed at known-vulnerable frameworks
- Sensitive file exposure — requests for .env, .git, backups and configuration files
- Request smuggling — conflicting Content-Length and Transfer-Encoding headers, which RFC 9112 says ought to be handled as an error
- Credential stuffing — high-volume login attempts using leaked username and password pairs
03What a WAF can and cannot cover in the OWASP Top 10
The OWASP Top 10, now in its 2025 edition, is often quoted as a WAF's job description, but only part of it is visible in a request. A WAF is strong against injection and helps with path traversal, exposed files and credential stuffing, while insecure design and most access-control flaws live in application logic that no request filter can see.
04Virtual patching: buying time after a disclosure
For a well-maintained application, a WAF's most valuable job is covering the gap between a vulnerability becoming public and your fix reaching production. It works best when an exploit needs one distinctive request feature: a rule that strips the internal header behind the Next.js middleware bypass from outside traffic neutralises it while the upgrade goes through testing. It works less well when the payload can be rephrased: early Log4Shell rules matched the plain exploit string, and within days attackers rebuilt it with Log4j's own lookup syntax to slip past them. Virtual patching covers when a rule holds and the bug classes it cannot reach.
05WAF vs. network firewall vs. DDoS protection
All three sit in front of a server and drop traffic, but at different layers and for different problems. Whether a firewall alone can hold off a flood is answered in can a firewall stop a DDoS attack.
| Network firewall | WAF | DDoS mitigation | |
|---|---|---|---|
| Layer | 3 – 4 (IP, TCP/UDP) | 7 (HTTP) | 3 – 7 |
| Decides on | IP, port, protocol | Request content and client | Volume and traffic shape |
| Stops | Access to closed ports | Application exploits | Floods and resource exhaustion |
| Example | Blocking port 22 externally | Blocking a SQLi payload | Absorbing a multi-gigabit flood |
06The real cost: false positives
The hard part of running a WAF is not catching attacks — it is not catching customers. A rule tuned aggressively enough to catch every exploit will eventually block a legitimate request that happens to contain a suspicious string, and blocking a paying customer usually costs more than letting a probe reach an application that was patched anyway.
CRS makes the trade-off explicit with paranoia levels. Level 1 is designed to cause few, ideally zero, false positives; level 2 suits sites handling real user data and expects some tuning; levels 3 and 4 accept many false positives in exchange for catching more. Every WAF has the same dial, labelled or not.
Two habits keep the cost down. Challenge rather than block when a request is ambiguous: a background check costs a real visitor a moment and forces simple automation to run a full browser, although headless browsers can pass it too, so it raises cost rather than proving anything. And run new rules in log-only mode first, then write exclusions for the legitimate cases; CRS recommends exclusions over editing rules, so updates do not overwrite your changes.
07How a WAF is deployed
There are three common models. A cloud WAF sits in front of your origin at DNS level, so traffic is inspected at the provider's edge. A host-based WAF runs as a module inside your own web server. An appliance is hardware or a virtual machine in your own network.
Cloud WAFs are the common choice for public websites because attacks are absorbed before they consume your bandwidth and the ruleset is maintained for you; the trade-off is that a third party's TLS handling becomes part of your security posture. For WordPress, the plugin versus cloud WAF comparison works through the same choice.
| Cloud WAF | Host-based WAF | Appliance | |
|---|---|---|---|
| Absorbs floods | Yes, before your uplink | No, attack traffic still arrives | Up to its own and your link's capacity |
| Ruleset upkeep | Provider | You | You or the vendor |
| TLS | Terminated at the edge | Inside your server | Needs your certificates and keys |
| Typical fit | Public websites, shops and APIs | Single servers, internal apps | Regulated on-premises estates |
08Checking a WAF after go-live
A WAF only protects traffic that passes through it, so the first checks after switching are about the path rather than the rules.
- Confirm the hostname resolves to the provider's edge and the response headers come from the proxy; the HTTP headers checker shows both from outside your network
- Send a harmless probe, like the first command below, and confirm it is blocked or challenged rather than answered by your application
- Make sure the origin answers only the provider, or every rule can be bypassed; hiding your origin IP covers the firewall side
- Restore real visitor addresses in your logs from the header the provider documents, trusting it only from the provider's ranges; nginx real IP shows the module settings and how to prove they work
# a harmless SQL-injection-shaped probe: expect a block or challenge, not your page
curl -s -o /dev/null -w '%{http_code}\n' "https://www.example.com/?id=1%27%20OR%20%271%27=%271"
# nginx at the origin, http level: did this connection come from the provider?
geo $realip_remote_addr $via_proxy {
default 0;
203.0.113.0/24 1;
}
# inside the site's server block: restore the visitor address, refuse direct hits
set_real_ip_from 203.0.113.0/24;
real_ip_header X-Forwarded-For;
if ($via_proxy = 0) { return 444; }Questions
Frequently asked questions
Sources
Sources and verification
The nginx fragment was syntax-checked with nginx 1.22.1 (nginx -t against a scratch config) and exercised with curl 7.88.1 in an isolated network namespace, with a loopback address standing in for the provider: a relayed request was served and logged under its X-Forwarded-For address, while direct connections, including one with a forged X-Forwarded-For, were closed without a response. The allow-and-deny variant in the warning was run in the same set-up against static content: the relayed request and the direct connections were all refused with 403. The probe command was run with the same curl against the test server. Debian 12, October 2026.
- 01OWASP Top 10:2025 · OWASP
- 02Exploitation of Log4j CVE-2021-44228 before public disclosure and evolution of evasion and exfiltration · Cloudflare
- 03CRS documentation: Anomaly Scoring · OWASP CRS
- 04CRS documentation: Paranoia Levels · OWASP CRS
- 05CRS documentation: False Positives and Tuning · OWASP CRS
- 06RFC 9112: HTTP/1.1 (sections 6.1 and 6.3, request smuggling) · IETF
- 07Microsoft Entra ID and PCI-DSS Requirement 6 (text of requirement 6.4.2) · Microsoft Learn
- 08FAQ 1593: superseded requirements after 31 March 2025 · PCI Security Standards Council
Last updated