Your developers just want to push code, run reviews, and forget about reverse proxy drama. Instead, they’re stuck diagnosing why Gitea refuses to behave behind HAProxy. One bad header or missing proxy setting, and suddenly webhooks fail or SSH access mysteriously times out. It’s a classic DevOps headache.
Gitea, the lightweight Git service, loves simplicity. HAProxy, the battle-tested load balancer, thrives on precision. Used together, they deliver private Git hosting that actually scales, with proper TLS, sticky sessions, and access control that won’t crumble under load. The trick is wiring them together in a way that keeps both fast and sane.
At its core, Gitea HAProxy integration does one thing: separates responsibility. Gitea focuses on repositories, webhooks, and identity. HAProxy takes care of routing, SSL termination, health checks, and traffic shaping. When you connect the two correctly, you get predictable URLs, reliable redirects, and a perimeter that makes compliance teams smile.
How it works
HAProxy listens publicly, handling HTTPS requests and forwarding them to one or more internal Gitea instances. It adds headers like X-Forwarded-Proto to preserve original connection context and can enforce strong client SSL policies or IP allowlists. Gitea, configured to trust the proxy, rebuilds the correct URLs for users and API responses. That clean separation improves security posture and simplifies scaling: new nodes register behind HAProxy with zero client disruption.
Best practices
Use sticky sessions only when you rely on in-memory session state. Prefer stateless authentication tokens via OIDC or OAuth2 for distributed teams. Keep HAProxy health checks lightweight and frequent to catch dead backends early. Most importantly, enable TLS 1.3 and restrict ciphers to modern suites, especially if you integrate with identity providers like Okta or Keycloak.
Gitea HAProxy troubleshooting tip
If users see mixed HTTP/HTTPS assets or fail to authenticate via your IdP, check reverse proxy headers. Ninety percent of misbehavior comes from missing or duplicated headers, not the app itself.