nginx, Apache 2 and subversion – 502 Bad Gateway error

All subversion checkouts, commits and other basic operations work as expected, but when attempting to copy, move or tag (copy) a (502 Bad Gateway) error presents itself.

greg@codemine:~/code/Foo (git-svn)-[trunk] %>; git svn tag foo-2.1.1
 Copying https://svn/projects/Foo/trunk at r18311 to https://svn/projects/Foo/tags/foo-2.1.1...
 RA layer request failed: Server sent unexpected return value (502 Bad Gateway) in response to COPY request for '/projects/!svn/bc/18311/Foo/trunk' at /usr/libexec/git-core/git-svn line 1123
Setup

There is a machine running nginx on port 80 (and 443 SSL) that serves as a reverse proxy for among other things subversion repositories. subversion runs on the backend on apache2.

The Problem

When performing a remote svn operation like MOVE or COPY the actual request is translated into a WebDAV request that looks similar to this:

COPY /svn/repos/oldname.txt HTTP/1.1
 Host: svn.example.org
 Destination: https://svn.example.org/svn/repos/newname.txt

Apache, the webserver, translates the above request to:

COPY https://svn.example.org/svn/repos/oldname.txt https://svn.example.org/svn/repos/newname.txt

It uses the Host parameter svn.example.org and the first request line COPY /svn/repos/oldname.txt HTTP/1.1 to assemble the source URL, https://svn.example.org/svn/repos/oldname.txt.

However, since requests are being reverse proxied through nginx, the source URL is changed from https:// to http:// as Apache listens on port 80 (HTTP). This results in a COPY operation that looks like this:

COPY http://svn.example.org/svn/repos/oldname.txt https://svn.example.org/svn/repos/newname.txt
Note the http:// instead of https://

Apache determines it can not move a file http://svn.example.org/svn/repos/oldname.txt to https://svn.example.org/svn/repos/newname.txt, for as far as Apache is concerned http://svn.example.org/ and https://svn.example.org/ are two entirely different hosts.  This results with a "502 Bad Gateway" error.

The Solution

Configure nginx to change the Destination header in the same way it changes the Host header; whereas, Apache can then handle the COPY or MOVE operation correctly. This is done by adding the following to your nginx configuration:

set $fixed_destination $http_destination;
 if ( $http_destination ~* ^https(.*)$ ) {
 set $fixed_destination http$1;
 }
 proxy_set_header Destination $fixed_destination;
Example Configs

/etc/nginx/sites-available/svn.example.org

server {  
    listen 80;
    server_name svn.example.org;
    rewrite ^(.*) https://svn.example.org$1 permanent;
}

server {  
    listen 443;
    server_name svn.example.org;

    ssl on;
    ssl_certificate /etc/nginx/conf.d/svn.example.org.crt;
    ssl_certificate_key /etc/nginx/conf.d/svn.example.org.key;
    ssl_verify_depth 3;

    client_max_body_size 100m;
    access_log /var/log/nginx/svn.example.org-access.log;

    location / {
        set $fixed_destination $http_destination;
        if ( $http_destination ~* ^https(.*)$ ) {
            set $fixed_destination http$1;
        }
        proxy_set_header Destination $fixed_destination;
        proxy_set_header Host $http_host;

        proxy_pass http://localhost:9080;
        proxy_connect_timeout 75;
        proxy_read_timeout 300;
        proxy_send_timeout 300;
    }
}

/etc/apache2/sites-available/svn.example.org

<VirtualHost *:9080>  
    ServerName svn.example.org
    ErrorLog /var/log/apache2/svn.example.org-error.log
    <Location />
        DAV svn
        DAVMinTimeout 0
        SVNParentPath /home/svn
        AuthType Basic
        AuthName "Subversion Repos"
        AuthUserFile /home/svn/.htpasswd
        AuthzSVNAccessFile /home/svn/conf/authz
        Require valid-user
    </Location>
</VirtualHost>


Source(s)

https://sigterm.sh/2012/10/09/nginx-apache-2-and-subversion-502-bad-gateway-error/