# An Attacker’s Perspective to WAF Evasion

**Published on:** 2026-07-18T00:00:00.000Z

**Updated on:** 2026-08-24T00:00:00.000Z

**Author:** Majd Abboud, Associate Ethical Hacker

[Web Application Firewalls (WAFs)](https://owasp.org/www-community/Web_Application_Firewall) are widespread, with many production web applications sitting behind one. This is intended to provide an extra layer of security for the application, protecting it from common web application attacks. And yet, skilled penetration testers bypass them all the time. Not because they are bad products, but because of the way they work at a basic level. They work by inspecting incoming requests and cross-checking them against a list of known malicious patterns. The gap in a WAF lies between what it checks for and what the backend actually processes, and that is where the opportunity for evasion lives.

This blog will cover several techniques that work in real-world engagements: encoding tricks, chunked request abuse, and header manipulation. More importantly, it explains why each one works, because understanding the logic behind a bypass will take you much further than simply running a tool and hoping for the best.

## **Understanding What a WAF Inspects**

Before bypassing anything, it is important to understand what you are actually dealing with. Modern WAFs look for a multitude of things and run [severa](https://www.indusface.com/blog/how-web-application-firewall-works/)l [detection methods](https://www.indusface.com/blog/how-web-application-firewall-works/) simultaneously.

**Pattern Matching:** Regex-based detection of known attack signatures such as

*   UNION SELECT
    
*   <script>
    
*   ../ (Traversal Sequences)
    

**Anomaly Detection:** One suspicious thing may not trigger a block, but a chain of suspicious activity will, as it deviates from typical traffic.

Behavioural Heuristics: Rate, timing and repetition patterns

*   A rapid burst of 403 from one IP is a signal.
    
*   A login attempt that arrives before the login page was ever loaded breaks the expected flow of normal user behaviour.
    
*   The same payload is sent over and over with minor variations.
    

[**ML-Based Detection**](https://arxiv.org/abs/2511.12643)**:** Newer WAFs score requests using trained models instead of fixed rules. Unlike signature matching, these models learn what normal traffic looks like and flag deviations from it, which means they can potentially catch novel attack variants that do not match any known pattern.

WAFs typically run inline, sitting as a reverse proxy or cloud service between the client and the original server, intercepting every request before it reaches the application. The key idea is that the WAF and the backend application are two separate pieces of software reading the same request. Every bypass takes advantage of a difference between how the WAF interprets things and how the application handles it. The goal is to find the difference, and you have your way in.

## **Encoding Tricks**

Encoding tricks are probably one of the most widely covered WAF evasion techniques, mostly because they have been around for a while, are easy to demonstrate, and may still be effective against some WAFs. The reason they work is simply that backends and WAFs do not always handle encoded characters the same way, and that gap is exploitable.

#### **URL Encoding and Double Encoding**

URL encoding works by turning special characters into percent-hex sequences, such as:

%27, which in this case is a single quote. Other special characters also have these percent-hex sequences. Most WAFs, however, decode these and check the result. The [double-encoding trick](https://owasp.org/www-community/Double_Encoding) gets around that: you encode the percent sign itself, turning %27 into %2527. If a WAF only decodes and checks one pass, the double-encoded payload can slip through. The back-end code then decodes a second time and gets the real payload.

> \# Standard payload - likely blocked
> 
> ?id=1' UNION SELECT version()-_\-_
> 
> \# Single URL encoded - still blocked if the WAF decodes URL encoding (most do)
> 
> ?id=1%27 UNION SELECT version()--
> 
> \# Double URL encoded - bypasses WAFs that only decode once
> 
> ?id=1%2527 UNION SELECT version()—

## **HTML Entity Encoding**

WAFs that scan request parameters for XSS payloads are usually looking for a literal script tag or similar markup keywords within the request. [HTML entity](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html) [encoding](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html) sidesteps that by representing those characters as entities instead of literal symbols, using either named character references or their numeric equivalents, shown below. If a filter is only matching on the literal angle brackets, none of those entity-encoded versions trip it, and the request goes through. From there, if the application reflects that value back into the page without re-encoding it, the browserʼs HTML parser decodes the entities while rendering, turning the encoded value right back into a live, executable script tag.

> \# Standard payload - likely blocked
> 
> ?q=<script>alert(1)</script>
> 
> \# HTML entity encoded - bypasses filters that don’t decode entities before matching
> 
> ?q=&lt;script&gt;alert(1)&lt;/script&gt;
> 
> \# Numeric character references - same condition, just a different entity format
> 
> ?q=&#60;script&#62;alert(1)&#60;/script&#62;

### **Unicode and Overlong Encoding**

This technique targets the same directory traversal sequences from earlier, just through a different decoding mismatch. Valid UTF-8 needs only one byte to represent an ASCII character like a forward slash or a period, but the encoding rules technically allow the same character to be represented using more bytes than necessary, an “[overlong](https://owasp.org/www-community/attacks/Unicode_Encoding)” encoding that a strict decoder should reject. Older or more permissive decoders accept it anyway and resolve it back to the original character, which is how an overlong-encoded slash famously decoded correctly on older IIS servers, shown below. If a filter is only watching for the literal traversal sequence or its single-encoded form, this slips right past it.

> \# Standard payload - likely blocked
> 
> ?file=../config.php
> 
> \# URL encoded - still blocked if the WAF decodes standard URL encoding (most do)
> 
> ?file=%2e%2e%2fconfig.php
> 
> \# Overlong UTF-8 encoded - bypasses decoders that accept non-canonical UTF-8
> 
> ?file=%c0%ae%c0%ae%c0%afconfig.php

###  **Chunked Request: Chunked Transfer Encoding**

[Chunked transfer encoding](https://arxiv.org/abs/2503.10846) is an actual HTTP feature that allows a request body to be sent in pieces, with each one prefixed with its size. The optimal way to abuse it is to split a dangerous keyword across the pieces sent in the request body. If the WAF were to inspect each chunk separately, it would only see harmless fragments like “UNI”, “ON”, “SEL”, and “ECT” as shown below. The backend, however, will reassemble them into a “UNION SELECT” and run it.

> POST /search HTTP/1.1
> 
> Host: target.example.com
> 
> Transfer-Encoding: chunked

> 3
> 
> UNI
> 
> 2
> 
> ON
> 
> 4
> 
>  SEL
> 
> 3
> 
> ECT
> 
> 0

This works because request assembly and signature matching are separate jobs and not every WAF does them in the correct order. Web application firewalls that scan chunks as they arrive are matching against fragments that mean nothing alone, while the origin server reassembles the body and executes the payload before the app ever sees a problem.

### **Header Manipulation: HTTP Request Smuggling**

Weʼve discussed smuggling payloads in the body, but headers are important to take a look at as well. HTTP headers are a separate part of the request from the body, and WAFs will often handle them with slightly less scrutiny.

One of the most powerful header manipulation techniques is called request smuggling. [HTTP](https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn) [request smuggling](https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn) is a technique used to interfere with the way a web application processes sequences of HTTP requests that are received from one or more users. In the scenario below, you send both a “Content-Length: 6” header, which basically says the body is exactly N bytes, count them and “Transfer-Encoding: chunked” header, which means to ignore byte counts. The body instead comes in labelled chunks and ends when you hit a chunk of size zero. If you notice, however, these are two different headers for finding the end of the same body, and by including both, a server has to pick one and ignore the other, and no rule ensures every server picks the same one.

> POST / HTTP/1.1
> 
> Host: target.example.com
> 
> Content-Length: 6
> 
> Transfer-Encoding: chunked
> 
> 0
> 
> G

The WAF will read “Content-Length: 6” and treat the request as if it were ending after 6 bytes, so it inspects that much and moves on. The backend follows “Transfer-Encoding”, hits the 0 marking at the end of the chunks, and then sees a leftover ‘Gʼ as the start of an entirely new request.

That disagreement, where the two systems read two different requests out of the same bytes, is how request smuggling happens. The ‘Gʼ here is just a proof of concept, and is simply one stray character that confirms the backend is reading something the WAF already stopped looking at. Once you confirm that works, you swap the ‘Gʼ out for your own request, like “GET /admin” or a block of malicious headers. Because the WAF quit reading at byte 6, anything you place past that point reaches the backend completely uninspected, so you are smuggling your own request straight through the filter.

### **Chaining the Techniques**

On their own, most of these techniques are well-known and covered by WAF vendors. The real bypasses happen when you combine them. A double-encoded payload, split across chunk boundaries, and wrapped in a request with conflicting length headers could score low enough on each check that the total never crosses the block threshold. Creating this type of bypass takes an understanding of each technique well enough to layer them without the pieces interfering with one another. Pulling that off is not generally as simple as pointing an automated tool at a target, and is often something that will be achieved by a proficient tester.

The value of manual testing becomes clear when you compare it to automated testing. A scanner fires known payloads one at a time and gets flagged the moment its anomaly scores climb. However, a skilled tester fingerprints the WAF first, learns how it decodes and scores requests, then builds a single tailored request that slips under every detector at once. Chaining techniques reward someone who takes the time to understand why they work, rather than someone hoping that fuzzing with a word list gets lucky.

That methodology is what separates a scanner from manual pentesting. You start by fingerprinting the WAF, identifying the vendor from its headers, error pages, and cookies, and learning how it decodes and scores requests. Then you map its detection boundaries by sending a known-blocked payload, applying one bypass technique at a time, and noting where the block happens each time. Only once you fully understand those edges do you construct a combined payload aimed at the specific gap you found, validate it quietly at low volume and pause the moment you have proof. A traditional automated scanner cannot do this. It cannot watch a target, adjust to what it learns, and craft something specific to that application. Patience and adaptability are the advantages of manual testing.

## **Conclusion**

WAFs are not broken. They do exactly what they are intended to do. They make attacks more expensive, stop the vast majority of automated threats, and give defenders time to respond. However, against a patient manual tester, WAFs can be more of a speed bump than a barrier. The techniques in this blog work not because WAF vendors are doing a bad job, but because HTTP is a very complex and ambiguous protocol and perfect normalization across all possible encoding, header combinations, and transfer formats is genuinely hard. Every spot where a [WAF and a backend](https://arxiv.org/abs/2503.10846) interpret the same data differently is a potential bypass, and finding those spots requires a human, not a script.

For defenders, the takeaway is straightforward. Clean WAF logs do not mean nothing is happening. It could mean the testing is just good. Layer in behavioural analytics, backend query logging and anomaly detection that does not entirely depend on the WAF. No single layer should be your last line of defence. The best security professionals think from the attacker' s perspective, not to harm, but because the only way to see the gaps in your defences is to look at them from the outside.
