You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
REQBODY_ERROR "Extra content at the end of the document" after 3.0.15 update #3563
I updated from version 3.0.14 to 3.0.15 today. Unfortunately, I don't have a ton of helpful info, but I've noticed a few errors that I haven't seen before with REQBODY_ERROR tripping.
Logs and dumps
Here's an example nginx error log (with IP and domain changed).
body.xml:1: parser error : Extra content at the end of the document
2026/05/12 20:51:59 [error] 22751#22751: *430298 [client 123.123.123.123] ModSecurity: Access denied with code 400 (phase 2). Matched "Operator `Eq' with parameter `0' against variable `REQBODY_ERROR' (Value: `1' ) [file "/etc/nginx/modsec/modsecurity.conf"] [line "53"] [id "200002"] [rev ""] [msg "Failed to parse request body."] [data "XML parsing error: XML: Failed to parse document."] [severity "2"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "example.com"] [uri "/"] [unique_id "177863351988.105338"] [ref "v269,1"], client: 123.123.123.123, server: example.com, request: "GET / HTTP/1.1", host: "example.com"
To Reproduce
Sorry, I don't know how to reproduce this. I've seen this a handful of times in millions of requests.
Server (please complete the following information):
ModSecurity version (and connector): ModSecurity v3.0.15 with nginx-connector v1.0.4
WebServer: nginx 1.26
OS (and distro): Amazon Linux 2
Rule Set (please complete the following information):
The mentioned blocks have changed since 3.0.14, but the last condition is the same (in both cases):
if (m_data.well_formed!=1) {
Sorry, I don't know how to reproduce this. I've seen this a handful of times in millions of requests.
Don't you have any audit.log with body part content? Without that, I'm afraid we can't investigate this issue. If you have a tons of this, probably that would be enough to allow audit.log (with body parts) for a short time.
I do have some audit logs, but I thought it was basically the same info as the error log. I posted it here anyway. We don't include the request or response headers or body for security reasons and PCI compliance. It can make debugging harder though.
Thanks for pointing out the updated code. I looked at some requests before and after upgrading. We did have some things triggering rule 200002 before and after, so I don't think it's an issue that the rule is triggering. That's probably appropriately flagging malicious requests. However, the difference is the body.xml:1: parser error : Extra content at the end of the document line in the nginx error logs. Notice that it doesn't output with the normal Nginx error log line prefix (e.g. 2026/05/12 20:51:59 [error] 22751#22751: *430298 [client 123.123.123.123] ). That is what brought this to my attention. We have code that parses timestamps from the logs, but these didn't have one.
I wonder if this needs an update similar to b9dc9cc.
I do have some audit logs, but I thought it was basically the same info as the error log. I posted it here anyway. We don't include the request or response headers or body for security reasons and PCI compliance. It can make debugging harder though.
I see and I agree.
But - I don't know how frequent is this issue, for a short period would it be possible to add the body parts (C) to SecAuditLogParts? Indeed, you might remove/change sensitive data.
Notice that it doesn't output with the normal Nginx error log line prefix (... ). That is what brought this to my attention. We have code that parses timestamps from the logs, but these didn't have one.
Ah, I see.
I wonder if this needs an update similar to b9dc9cc.
I do have some audit logs, but I thought it was basically the same info as the error log. I posted it here anyway. We don't include the request or response headers or body for security reasons and PCI compliance. It can make debugging harder though.
I see and I agree.
But - I don't know how frequent is this issue, for a short period would it be possible to add the body parts (C) to SecAuditLogParts? Indeed, you might remove/change sensitive data.
It's so infrequent. In millions of requests yesterday, there were probably no more than 1 - 2 dozen. I can't enable the logging because PCI compliance requires log retention and deletion protection. Even if that wasn't a concern, I'm pretty sure the sheer volume of ModSecurity blocks I deal with would fill up our disk.
I think so. It's just the error log entry without the date prefix that's problematic.
Notice that it doesn't output with the normal Nginx error log line prefix (... ). That is what brought this to my attention. We have code that parses timestamps from the logs, but these didn't have one.
Ah, I see.
I wonder if this needs an update similar to b9dc9cc.
@airween, I found a really easy way to duplicate it. I ran the following against my server and saw it in the logs. I tried to use the CRS sandbox so it was a server you'd be able to see, but the command below results in a 500 server error.
Sorry, I closed the window already, but it included body.xml:1: parser error : Extra content at the end of the document like in the initial issue comment. If you want me to run it again, let me know.
Revisiting this one. The XML processor itself did not change between 3.0.14 and 3.0.15 except for 624a0de ("Fix libxml2 related deprecated issues", October 2025), which replaced the deprecated libxml2 calls, and that is the candidate for a behaviour difference: the body.xml:1: parser error lines are libxml2's own error output, which previous builds did not surface the same way. Both reports show a GET with an XML Content-Type and an empty or non-XML body, where "Document is empty" / "Extra content" is what libxml2 says.
To settle it we need one captured request (headers plus raw body) that passes on 3.0.14 and fails on 3.0.15. Pending feedback.
Thanks @ssigwart, that comparison settled it. Root cause: 3.0.15 replaced the old null xmlSetGenericErrorFunc() handler with a direct ctxt->options |= XML_PARSE_NOERROR | XML_PARSE_NOWARNING. libxml2 only reads flags stored that way from 2.13 on; 2.9 through 2.12 (Amazon Linux 2023 ships 2.10) apply them inside xmlCtxtUseOptions(), so on those versions the flags were a no-op and libxml2 kept printing to stderr. That is also why it could not be reproduced on a newer distro.
Fix in #3677: call xmlCtxtUseOptions() instead. Verified against libxml2 2.9.13 and 2.15.4.
Activity
airween commentedon May 13, 2026
Hi @ssigwart,
thanks for reporting this.
Based on the message in the attached log this can happened at two places:
The mentioned blocks have changed since 3.0.14, but the last condition is the same (in both cases):
Don't you have any audit.log with body part content? Without that, I'm afraid we can't investigate this issue. If you have a tons of this, probably that would be enough to allow audit.log (with body parts) for a short time.
ssigwart commentedon May 13, 2026
Hi @airween,
I do have some audit logs, but I thought it was basically the same info as the error log. I posted it here anyway. We don't include the request or response headers or body for security reasons and PCI compliance. It can make debugging harder though.
Thanks for pointing out the updated code. I looked at some requests before and after upgrading. We did have some things triggering rule 200002 before and after, so I don't think it's an issue that the rule is triggering. That's probably appropriately flagging malicious requests. However, the difference is the
body.xml:1: parser error : Extra content at the end of the documentline in the nginx error logs. Notice that it doesn't output with the normal Nginx error log line prefix (e.g.2026/05/12 20:51:59 [error] 22751#22751: *430298 [client 123.123.123.123]). That is what brought this to my attention. We have code that parses timestamps from the logs, but these didn't have one.I wonder if this needs an update similar to b9dc9cc.
airween commentedon May 13, 2026
I see and I agree.
But - I don't know how frequent is this issue, for a short period would it be possible to add the body parts (
C) to SecAuditLogParts? Indeed, you might remove/change sensitive data.So probably this is a normal behavior?
Ah, I see.
Thanks, I'll take a look at that.
ssigwart commentedon May 13, 2026
It's so infrequent. In millions of requests yesterday, there were probably no more than 1 - 2 dozen. I can't enable the logging because PCI compliance requires log retention and deletion protection. Even if that wasn't a concern, I'm pretty sure the sheer volume of ModSecurity blocks I deal with would fill up our disk.
I think so. It's just the error log entry without the date prefix that's problematic.
Thank you.
ssigwart commentedon May 13, 2026
@airween, I found a really easy way to duplicate it. I ran the following against my server and saw it in the logs. I tried to use the CRS sandbox so it was a server you'd be able to see, but the command below results in a 500 server error.
airween commentedon May 13, 2026
@ssigwart,
thanks, I was able to reproduce that.
But I got only this in my error.log:
Nothing more.
libmodsecurity3 is the current upstream (Github) version, connector is 1.0.4 (stable released).
ssigwart commentedon May 14, 2026
It took me a while, but I was able to reproduce this in docker. Here's what I did.
Then, from outside of docker, I ran this:
airween commentedon May 14, 2026
Thanks for the detailed explanation.
What did you exactly get in the error log then?
ssigwart commentedon May 14, 2026
Sorry, I closed the window already, but it included
body.xml:1: parser error : Extra content at the end of the documentlike in the initial issue comment. If you want me to run it again, let me know.zffocussss commentedon Sep 11, 2026
ModSecurity 3.0.16 is also producing the similar errors.
body.xml:1: parser error : Extra content at the end of the document
body.xml:1: parser error : Document is empty
airween commentedon Sep 11, 2026
Hi @zffocussss,
could you help us to share a minimal example with we can reproduce the issue?
fzipi commentedon Oct 12, 2026
Revisiting this one. The XML processor itself did not change between 3.0.14 and 3.0.15 except for 624a0de ("Fix libxml2 related deprecated issues", October 2025), which replaced the deprecated libxml2 calls, and that is the candidate for a behaviour difference: the
body.xml:1: parser errorlines are libxml2's own error output, which previous builds did not surface the same way. Both reports show aGETwith an XML Content-Type and an empty or non-XML body, where "Document is empty" / "Extra content" is what libxml2 says.To settle it we need one captured request (headers plus raw body) that passes on 3.0.14 and fails on 3.0.15. Pending feedback.
ssigwart commentedon Oct 13, 2026
I followed the steps in #3563 (comment). The output is this:
I then repeated it, but changed these lines (
pcreinstead ofpcre2and 3.0.14 instead ofv3/master):The output there is:
fzipi commentedon Oct 13, 2026
Thanks @ssigwart, that comparison settled it. Root cause: 3.0.15 replaced the old null
xmlSetGenericErrorFunc()handler with a directctxt->options |= XML_PARSE_NOERROR | XML_PARSE_NOWARNING. libxml2 only reads flags stored that way from 2.13 on; 2.9 through 2.12 (Amazon Linux 2023 ships 2.10) apply them insidexmlCtxtUseOptions(), so on those versions the flags were a no-op and libxml2 kept printing to stderr. That is also why it could not be reproduced on a newer distro.Fix in #3677: call
xmlCtxtUseOptions()instead. Verified against libxml2 2.9.13 and 2.15.4.fzipi commentedon Oct 13, 2026
Follow-up for keeping the libxml2 message (instead of discarding it once #3677 silences stderr): #3678.