nginx, Apache 2 and subversion - 502 Bad Gateway error

The Problem

I recently ran into this problem and couldn’t find any useful information on the net around fixing it. All subversion checkouts, commits and other basic operations work just fine, but when attempting to copy, move or tag (copy) I would get the below (502 Bad Gateway) error.

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

Our Setup

We have a machine running nginx on port 80 (and 443 SSL) that serves up a bunch of development tools - buildbots, Jenkins, git repos and of course a few subversion repos. Behind it we have an Apache2 server that serves up subversion repos over WebDAV.

nginx simply reverse proxies requests to the backend Apache 2 servers and handles the SSL termination as our repos are only available over SSL (https).

What’s Really Happening

The actual problem is not really that obvious.

When you perform 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 quickly figures out that it can’t move a file http://svn.example.org/svn/repos/oldname.txt to https://svn.example.org/svn/repos/newname.txt, because as far as Apache is concerned http://svn.example.org/ and https://svn.example.org/ are two entirely different hosts.

… and you end up with a “502 Bad Gateway” error.

The Solution

The solution to this problem is getting nginx to change the Destination header in the same way it changes the Host header. 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;

Complete Configs

For reference purposes I’ve included the complete configs below.

/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>
Gregory Armer avatar
About Gregory Armer
Sometimes I just want to give it all up and become a handsome billionaire.
comments powered by Disqus