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.%2f
doesn't get decoded to/
if we setdecode_percent_characters off;
innginx.conf
);
Example
Show/Hide example
Suppose we have a file /usr/local/nginx/html/path%2fslash/index.html
:
<!DOCTYPE html>
<html>
<head>
<title>Percent-encoded test</title>
</head>
<body>
<h1>Percent-encoded test</h1>
</body>
</html>
File /usr/local/nginx/conf/nginx.conf
:
worker_processes 1;
events {
worker_connections 1024;
}
http {
decode_percent_characters off;
server {
listen 80;
server_name localhost;
}
}
Test request:
curl "localhost:80/path%2fslash/"
Result:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx!</h1>
</body>
</html>
Without this change we would get a 404: Not Found
error, and the following log entry in /usr/local/nginx/logs/error.log
:
2025/02/07 12:15:26 [error] 103512#0: *9 "/usr/local/nginx/html/path/slash/index.html" is not found (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /path%2fslash/ HTTP/1.1", host: "localhost"
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) |
- |
Fastify (JS) | 🟢 | Using :id (default) |
- |
Actix Web (Rust) | 🟢 | Using {id} (default) |
- |
ASP.NET Core (C#) | 🟢 | Using {id} (default) |
- |
Spring Boot (Java) | 🟢 | By default requests containing %2F in path parameters return 400: Bad Request , but Tomcat can be configured to allow them |
- |
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_characters
is true, then thengx_http_parse_complex_uri()
function doesn't decode%
characters (when encountering them it simply do not entersw_quoted
status); - 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
?