The dangerous line is not in Gitea's code. It sits in the default configuration its official Docker image ships with. That image sets REVERSE_PROXY_TRUSTED_PROXIES to a wildcard, so the moment an operator turns on reverse-proxy login, the server will believe an identity header sent from any address that can reach it. Researchers reported the first attempt against this in the wild this week, 13 days after the flaw went public, and that gap is the reason to move now rather than at the next maintenance window.
The bug is tracked as CVE-2026-20896, rated 9.8, and credited to researcher Ali Mustafa. It affects Gitea's Docker images through 1.26.2 and is fixed in 1.26.3, released in late June. If you run the container and never enabled reverse-proxy authentication, this specific default does not expose you. Everyone who did enable it is a candidate for full account takeover, and admins are the obvious target.
How does a login header become a full authentication bypass?
Gitea can accept an already-authenticated username from a trusted front-end through the X-WEBAUTH-USER header. It is supposed to trust that header only when the request comes from the proxy in front of it. The Docker image trusts it from every source IP, so any caller who reaches the port directly can name any account and be logged in as that user, no password and no token required.
What makes this a shipped bug rather than an operator mistake is where the two defaults disagree. Gitea's documented sample config restricts the trusted range to loopback. The container image overrides that with a wildcard, and most operators never notice the difference until someone points it out.
| Setting | Documented default | Official Docker image (through 1.26.2) |
|---|---|---|
| REVERSE_PROXY_TRUSTED_PROXIES | loopback only (localhost) | * (every source IP) |
| Who can set X-WEBAUTH-USER | the local reverse proxy only | anything that reaches the container port |
| Effect with reverse-proxy auth on | header honored from the front-end | log in as any user, no password |
This is the same own-goal we keep writing up: a self-hosted tool that optimizes the five-minute quickstart at the cost of the default security posture. Crawl4AI shipped its server unlocked in exactly this shape, and LangSmith trusted a client-supplied header it should have ignored. The lesson for anyone running Gitea alongside other self-hosted infrastructure is to treat the container's shipped defaults as untrusted, not as a hardened baseline.
Are you behind the proxy, or just standing next to it?
The quiet assumption in every reverse-proxy auth setup is that the backend is only reachable through the proxy. Docker breaks that assumption more often than people expect. A docker-compose.yml that publishes port 3000 to the host, or a bridge network another container can route to, means the Gitea port is reachable without ever passing the authenticating front-end. The header trust then applies to that direct path too.
So the real exposure question is not whether you run a proxy in front of Gitea, but whether anything else can reach the Gitea port. If the answer is yes, the wildcard turns every reachable network into an authentication bypass. Self-hosted Git servers are a recurring soft target for this class of mistake: we saw it with GitLab's no-login session flaw and with Gogs trusting its logged-in users too far. A code-hosting server holds your source, your CI secrets, and often your deploy keys, which is why ranking this kind of exposure ahead of a routine CVE is the right call.
How would you know if someone already tried?
Patching closes the hole. It does not tell you whether the two-week window before you patched was clean. According to reporting from Sysdig relayed by The Hacker News, the first observed probe came from a commercial VPN exit and stayed in reconnaissance, which reads as opportunistic mass-scanning rather than a targeted operation. That pattern means anyone with an exposed Gitea instance is in scope, not just high-value targets.
The detection that matters here is simple to describe: an inbound request carrying X-WEBAUTH-USER that did not transit your authenticating proxy is illegitimate by definition. If your front-end is the only thing meant to set that header, then the same header arriving on the direct container port, or from any source that is not your proxy address, is a probe or an exploit attempt. Feeding your proxy and container access logs into a place you can query them together lets you answer whether anyone tried this instead of guessing. A managed detection layer over your own logs is meant to surface exactly that mismatch and explain it in plain language rather than leaving you to spot a header anomaly by eye.
Upgrade to 1.26.3, then stop trusting the header blindly
Move to Gitea 1.26.3 or later. The patched image drops the wildcard, so the header is trusted only from where you tell it to be, matching what the documentation always described. If you cannot upgrade immediately, the interim control is to set REVERSE_PROXY_TRUSTED_PROXIES to your proxy's address only, and to have that proxy strip any client-supplied X-WEBAUTH-USER on inbound so a request can never carry its own identity claim.
The durable takeaway is broader than one CVE. A backend should never trust an identity header unless the only path to that backend is through the component that sets it, and that component clears the client's copy first. That rule holds for Gitea, for oauth2-proxy fronting a dashboard, for any app that reads X-Forwarded-User. Where a header can arrive from more than one path, header-based auth is not authentication. It is a suggestion the server chose to believe.