Hello Gitea Community,
I am currently experiencing a persistent configuration issue while hosting my website’s source code and deployment workflows on a self-managed Gitea instance, and the problem appears to be directly related to reverse proxy setup and external URL configuration. The core issue is that although Gitea works perfectly when accessed via localhost or internal network IP, external users accessing the instance through my domain encounter incorrect redirects, broken links, and failed repository cloning attempts. Specifically, the application consistently generates URLs using the internal server address instead of the public-facing domain, which results in authentication failures and unusable web interface navigation when accessed outside the local network.
The Gitea instance is running behind an Nginx reverse proxy with HTTPS termination enabled. I have configured the proxy to forward traffic to the internal Gitea service running on a different port, and the basic routing works as expected. However, despite setting the ROOT_URL parameter in the app.ini configuration file to match my public domain, Gitea continues to produce links and clone URLs referencing the internal hostname. This behavior causes issues particularly when users attempt to clone repositories via HTTPS, as Git attempts to connect to the internal address which is not resolvable externally. I have restarted the service multiple times after updating the configuration, but the issue remains unchanged.
I suspect that the issue may be related to mismatched protocol headers or missing proxy headers in the Nginx configuration. While I have included standard X-Forwarded-For and X-Forwarded-Proto headers, it is possible that something is not being interpreted correctly by Gitea, resulting in confusion about whether the application is operating behind HTTPS. The instance sometimes forces redirects to HTTP instead of HTTPS, creating mixed content warnings in the browser. Even after reviewing documentation and comparing recommended reverse proxy configurations, I cannot identify why Gitea does not consistently respect the externally configured domain and protocol settings.
Another aspect of the issue is that webhook URLs generated for my website’s deployment automation are also incorrect. When repositories trigger webhooks to deploy updates to the website, the callback URLs use the internal address rather than the public domain, which breaks automated deployment workflows. This indicates that the misconfiguration is systemic within Gitea’s URL generation logic rather than isolated to repository clone URLs. Since these URLs are dynamically generated based on application configuration, it suggests that either cached configuration values or environment-level overrides may be interfering with the app.ini settings.
Additionally, I have verified that DNS records are correctly pointed to the server’s public IP and that SSL certificates are valid and functioning. Direct access to other services behind the same reverse proxy works without issues, which makes it unlikely that the problem lies in the DNS or TLS setup itself. The only component exhibiting inconsistent behavior is Gitea’s URL resolution and redirect handling. Clearing browser caches and testing from different client networks produces identical results, further reinforcing that the problem is not client-side but related to how Gitea interprets its deployment environment.
I am seeking guidance from the Gitea community regarding best practices for correctly configuring a reverse proxy setup so that Gitea consistently generates public-facing URLs, respects HTTPS termination, and ensures repository clone links and webhook endpoints function properly for external users. Specifically, I would appreciate clarification on required proxy headers, necessary app.ini parameters beyond ROOT_URL, and any common pitfalls when deploying Gitea behind Nginx. My ultimate goal is to ensure that my website’s code repositories and deployment automation workflows operate seamlessly for all users without internal hostname leakage or protocol mismatches. Sorry for long post!