feature: adding a configuration flag to set whether NGiNX should decode percent-encoded characters in URI or not
@pluknet is already working on this.
Since Apr 28, 2026.
- Dominant language
- C
- Stars
- 31.7k
- Forks
- 8.3k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 13
Description
[!NOTE]
I'm not a security expert and I don't know if that feature might generate vulnerabilities or anything similar (e.g. path traversal attacks). If that's the case, you can directly close or delete the issue, but I'd love to be redirected to some relevant documentation on the topic and/or to receive some guidance on how to approach this task in the correct way 🙂
Describe the feature you'd like to add to nginx
I'd like to introduce a configuration flag named decode_percent_characters (or something similar):
- by default set to
on, so that it doesn't change NGiNX default behaviour; - similarly to
merge_slashes, it's passed tongx_http_parse_complex_uri(); - if set to
off, NGiNX doesn't replace percent-encoded characters in request URIs, with their respective decoded values (i.e.%2fdoesn't get decoded to/if we setdecode_percent_characters off;innginx.conf);
Example
Show/Hide example
Suppose we compiled Nginx from source (files located at /usr/local/nginx/) and we are serving 2 pages:
-
/usr/local/nginx/html/path%2fslash/index.html:Path: path%2fslash/ -
/usr/local/nginx/html/path/slash/index.html:Path: path/slash/
Before this feature
Nginx config file (/usr/local/nginx/conf/nginx.conf):
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
}
}
Test requests:
-
Encoded slash:
$ curl localhost/path%2fslash/ Path: path/slash/ -
Slash:
$ curl localhost/path/slash/ Path: path/slash/
Logs (/usr/local/nginx/logs/access.log):
127.0.0.1 - - [28/Apr/2026:15:42:59 +0200] "GET /path/slash/ HTTP/1.1" 200 18 "-" "curl/8.5.0"
127.0.0.1 - - [28/Apr/2026:15:42:59 +0200] "GET /path/slash/ HTTP/1.1" 200 18 "-" "curl/8.5.0"
NB: It always returns the page at path/slash (%2f is automatically decoded to / before matching the route).
With new feature
worker_processes 1;
events {
worker_connections 1024;
}
http {
decode_percent_characters off;
server {
listen 80;
server_name localhost;
}
}
Test requests:
-
Encoded slash:
$ curl localhost/path%2fslash/ Path: path%2fslash/ -
Slash:
$ curl localhost/path/slash/ Path: path/slash/
Logs (/usr/local/nginx/logs/access.log):
127.0.0.1 - - [28/Apr/2026:15:43:54 +0200] "GET /path%2fslash/ HTTP/1.1" 200 20 "-" "curl/8.5.0"
127.0.0.1 - - [28/Apr/2026:15:43:57 +0200] "GET /path/slash/ HTTP/1.1" 200 18 "-" "curl/8.5.0"
NB: it serves the correct pages.
Describe the problem this feature solves
With this feature, we would be able to serve pages containing percent-encoded characters in their path (e.g. /usr/local/nginx/html/path%2fslash/index.html).
Additionally, when NGiNX is used as a reverse proxy, such as when it's distributed via OpenResty and used as an API Gateway (e.g. APISIX, Kong, etc.), we can match routes that have path parameters containing percent-encoded characters, and delegate their decoding to upstreams/other web servers.
Notice that many popular web frameworks already support percent-encoded characters in path parameters:
| Technology | Supports %-encoded chars | Usage | Docs |
|---|---|---|---|
| Django (Python) | 🟢 | Using <path:id> |
Django - Path Converters |
| Fastapi (Python) | 🟢 | Using {id:path} |
Fastapi - Path Convertor |
| Flask (Python) | 🟢 | Using <path:id> |
Flask - Variable Rules |
| Express (JS) | 🟢 | Using :id (default) |
Express - Route Parameters |
| Fastify (JS) | 🟢 | Using :id (default) |
Fastify - URL Building |
| Actix Web (Rust) | 🟢 | Using {id} (default) |
Actix - Resource Pattern Syntax |
| ASP.NET Core (C#) | 🟢 | Using {id} (default) |
ASP.NET - Route Templates |
| Spring (Java) | 🟢 | By default requests containing %2F in path parameters return 400: Bad Request, but Tomcat can be configured to allow them |
Spring MVC - URI Patterns |
| NGiNX (C) | 🔴 | - | - |
Additional context
Some References
- pass_proxy subdirectory without url decoding, from a StackOverflow answer
- nginx proxy_pass and URL decoding, , from a StackOverflow answer
- super old NGiNX closed ticket: Nginx pass_proxy subdirectory without url decoding
- workaround: encode
%in request URIs, from a StackOverflow answer;
Feature Implementation
I already implemented two versions of this feature in a fork:
- flag
decode_percent_characters, to handle decoding of all percent-encoded characters: mikyll/nginx:http_parse_complex_uri_decode_percent. It's pretty straightforward, ifdecode_percent_charactersis true, then thengx_http_parse_complex_uri()function doesn't decode%characters (when encountering them it simply do not entersw_quotedstatus); - flag
decode_slashes, to handle decoding of only%2f(slash/) character: mikyll/nginx:http_parse_complex_uri_decode_slashes
APISIX Example
With this feature, we could make APISIX correctly match routes containing %-encoded characters in path parameters (when using router radixtree_uri_with_parameters, see APISIX Docs | Router), by simply setting the following configuration in /apisix/path/conf/config.yaml:
nginx_config:
http_configuration_snippet: |
decode_percent_characters off;
OpenResty
Maybe there's a simpler way to handle this via OpenResty ngx_http_lua_module?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.