Repository navigation
Subrequest auth (TARGET=" ") returns 400 for every multipart POST #1995
Description
Describe the bug
Anubis in subrequest-auth mode answers 400 Bad Request to the check endpoint whenever the forwarded request has a multipart/form-data Content-Type.
Reverse proxies doing forward auth (Traefik forwardAuth, nginx auth_request, Caddy forward_auth) copy the original request's headers to the check subrequest but not its body. ServeHTTPNext now calls prepareChallengeForm, which runs r.ParseMultipartForm on that empty body. The parse fails, and the error is turned into a hard 400 before the redir logic runs.
The proxy relays the 400 to the client, so every multipart form POST on a protected site fails. JSON and urlencoded POSTs and all GETs keep working, which makes the breakage look random from the application side.
Steps to reproduce
docker network create anubis-repro
docker run -d --name anubis-repro --network anubis-repro \
-e BIND=:8080 -e 'TARGET= ' ghcr.io/techarohq/anubis:main
# Simulates what a forward-auth proxy sends for a multipart POST:
# original headers, no body.
docker run --rm --network anubis-repro curlimages/curl -s -w ' %{http_code}\n' -o /dev/null \
-H 'X-Forwarded-Method: POST' \
-H 'X-Forwarded-Proto: https' \
-H 'X-Forwarded-Host: example.com' \
-H 'X-Forwarded-Uri: /upload' \
-H 'X-Real-Ip: 203.0.113.7' \
-H 'Content-Type: multipart/form-data; boundary=x' \
http://anubis-repro:8080/.within.website/x/cmd/anubis/api/checkResults:
| Image | multipart/form-data |
application/json |
no Content-Type |
|---|---|---|---|
:main (d60d8a8) |
400 Bad Request |
200 | 200 |
:v1.28.0-pre2 |
200 | 200 | 200 |
Expected: 200, the same as for the other content types. The check endpoint has no business reading the proxied request's body.
Actual: 400 Bad Request (text/plain) from prepareChallengeForm.
Expected behavior
200, the same as for the other content types. The check endpoint has no business reading the proxied request's body.
Your operating system and its version.
Linux 6.18.33 #1-NixOS
Your browser and its version.
Firefox 156
Additional context
If I had to guess this seems like the culprit
lib/http.go at d60d8a8:
func (s *Server) ServeHTTPNext(w http.ResponseWriter, r *http.Request) {
if s.next == nil {
w.Header().Set("Cache-Control", "no-store")
if !prepareChallengeForm(w, r) { // added in #1989
return
}
defer cleanupChallengeForm(r)
...
redir := r.FormValue("redir")lib/challenge_form.go:
func prepareChallengeForm(w http.ResponseWriter, r *http.Request) bool {
r.Body = http.MaxBytesReader(w, r.Body, 64<<10)
err := r.ParseForm()
if err == nil {
err = r.ParseMultipartForm(64 << 10)
}
if err == nil || errors.Is(err, http.ErrNotMultipart) {
return true
}
...
http.Error(w, http.StatusText(status), status) // -> 400
return false
}With Content-Type: multipart/form-data; boundary=… and an empty body, ParseMultipartForm returns a multipart read error (not http.ErrNotMultipart), so the request is rejected.
Before #1989, this branch only called r.FormValue("redir"). That also parses multipart forms, but it ignores parse errors, so the check went through.
prepareChallengeForm makes sense for MakeChallenge and PassChallenge, which really do get form posts from the challenge page. In ServeHTTPNext's subrequest branch, though, the request is a proxied auth check whose body (if any) belongs to the upstream application.
Activity
asta-kassel commentedon Oct 7, 2026
Thanks for the write up up :). This was quite hard to debug why our php application broke on the newest update. First I suspected our php application and then traefik. Our application allows it to upload files and only that was broken. Only afterwards I checked anubis.
levank311-pixel commentedon Oct 8, 2026
levank311-pixel commentedon Oct 8, 2026
Nhận thưởng
levank311-pixel commentedon Oct 8, 2026
levank311-pixel commentedon Oct 8, 2026
Xát nhận
levank311-pixel commentedon Oct 8, 2026
#2003
tari commentedon Oct 11, 2026
My site only seems to have an issue with this on one path (that I know of so far), but I confirmed that both 1.28.0 and 1.28.1 are returning 400 errors for request authentication while anubis 1.27.0 does not.
chrl-p commentedon Oct 12, 2026
I can confirm that I'm experiencing the same bug. Is there a temporary fix? I haven't been able to allow POST requests using a botPolicy.
Xe commentedon Oct 12, 2026
Please try running the main branch docker image when CI passes after #2026 is merged.
This requires a patch to be fixed, I am not aware of a workaround other than changing your nginx configuration to not create this exact circumstance.
chrl-p commentedon Oct 13, 2026
Thanks for the fix and your quick reply @Xe! I’ve deployed the latest ‘main’ image and the upload is working properly; that’s perfect! Do you have any idea when this might be merged into the ‘latest’ image?
ne20002 commentedon Oct 24, 2026
I believe this is fixed with Anubis 1.28.2.
tari commentedon Oct 24, 2026
Yes, 1.28.2 contains #2026 and I've confirmed on my setup (using nginx
auth_request) that file uploads work with that version.