GitLab behind an Apache reverse proxy
Historical note: The Unicorn-specific backend from 2018 is obsolete; this page is for historical understanding.
This note records an older Omnibus GitLab setup where GitLab bundled NGINX was disabled and Apache acted as the external reverse proxy. The historical backend was Unicorn; current GitLab architecture has changed, so the old backend-specific directives must not be copied to a modern installation.
Historical layout
output 7 lines
Client | v Apache HTTPS virtual host | v GitLab application backend on loopback
Conceptual Apache configuration:
<VirtualHost *:443>
ServerName gitlab.example.com
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
ProxyPass /gitlab/ http://127.0.0.1:8181/gitlab/
ProxyPassReverse /gitlab/ http://127.0.0.1:8181/gitlab/
</VirtualHost>The actual backend endpoint must come from the GitLab version being deployed.
Important modern caveat
Do not use old unicorn[...] configuration on current GitLab. GitLab moved from Unicorn to Puma, and modern Omnibus packages also use GitLab Workhorse in the request path.
For a current deployment, follow the GitLab documentation for external_url, relative URL support, bundled NGINX versus an external web server, trusted proxy/forwarded headers, WebSockets and large uploads. Validate redirects, clone URLs, artifact downloads and API endpoints after changing reverse-proxy behavior.
References
- GitLab documentation: https://docs.gitlab.com/
- GitLab Omnibus configuration: https://docs.gitlab.com/omnibus/settings/configuration.html
- Apache mod_proxy: https://httpd.apache.org/docs/2.4/mod/mod_proxy.html
Current practice (checked 2026-10)
- Backend: GitLab documents Puma as the default web server and Unicorn as no longer supported. On a current Linux package installation Apache proxies to GitLab Workhorse, not to the application server; the
127.0.0.1:8181endpoint shown above is the Workhorse TCP listener. - gitlab.rb for an external web server: set
nginx['enable'] = false,web_server['external_users'](the Apache user),gitlab_rails['trusted_proxies'], and for Apachegitlab_workhorse['listen_network'] = "tcp"withgitlab_workhorse['listen_addr'] = "127.0.0.1:8181", then rungitlab-ctl reconfigure. - Support: GitLab treats troubleshooting of a non-bundled web server as outside the scope of support. Keep the bundled NGINX unless there is a firm reason for Apache.
- Headers: the bundled NGINX sends both
X-Forwarded-Proto: httpsandX-Forwarded-Ssl: on; set both in Apache.X-Forwarded-For,X-Forwarded-HostandX-Forwarded-Serverare added by mod_proxy itself (ProxyAddHeaders Onis the default). - Encoded paths: the GitLab API uses URL-encoded project paths (
%2F). The GitLab Apache recipe setsAllowEncodedSlashes NoDecode; withProxyPassalso addnocanonso the path reaches Workhorse unmodified. - WebSockets: since httpd 2.4.47 mod_proxy_http tunnels protocol upgrades itself with the
ProxyPassparameterupgrade=websocket; the older mod_rewrite rules tows://are no longer needed. - TLS: the conceptual virtual host has no TLS directives. Do not take them from the old gitlab-recipes Apache file (
SSLProtocol all -SSLv2and 3DES suites, last tested on GitLab 13.8); allow only TLS 1.2 and 1.3, for exampleSSLProtocol -all +TLSv1.2 +TLSv1.3. - Relative URL:
/gitlab/style installs still work throughexternal_url "https://example.com/gitlab", but GitLab recommends its own (sub)domain and lists known issues with Geo and testing limitations for relative URLs.
Sources: