Home > Tools > Git > Host a Git repository over HTTP(S) with limited access to the (...)

Host a Git repository over HTTP(S) with limited access to the server

Git with a .htaccess and a CGI script

Tuesday 10 January 2012

Since version 1.6.6, Git is able to tunnel its native protocol through HTTP or HTTPS, which makes roughly as efficient over HTTP than it is over SSH. This is often called “smart HTTP”. While I like SSH, I sometimes have co-workers behind a firewall, who do not have SSH access. In these cases, HTTP is the solution.

Standard installation instructions for smart HTTP are available in man git-http-backend or in Pro Git, but they often assume that one has a shell account on the server, and/or is the administrator of the server. This document explains how to work with just a user access to the server, just uploading files (this document was written and tested with direct NFS access, but ftp/rsync/whatever should work too), including .htaccess, and running CGI scripts (this obviously assumes the server is configured to accept .htaccess and running CGI-scripts, and will be easier if Git is installed on the server). This has been tested with apache, and probably won’t work with any other webserver.

(Note that these instructions do not require shell access to the server, but security-wise, the ability to run arbitrary CGI scripts is essentially equivalent to a shell access)

 Setting-up CGI Scripts

First, create a directory in which you’re going to host the repository. In this directory, create a file .htaccess with the content :

Options +ExecCGI
AddHandler cgi-script cgi

This way, any file ending with .cgi will be considered as a CGI script, and the webserver will execute it to serve it.

 Getting some configuration information

Now, we’ll get some information about the server. Write a small config.cgi script containing this:

#! /bin/sh

echo 'Content-type: text/plain'

git --version

and make sure the file is executable (chmod +x config.cgi if needed). Now, open the corresponding URL in your browser, you should get something like this:

git version

The first line is the absolute path to your hosting directory on the server, and the second ensures Git is installed on the server and shows you which version.

 Setting-up Authentication

We’re going to make a private repository, hence need authentication. We’ll rely on the Apache authentication mechanism, at the HTTP level.

Now that we know the absolute path of our directory, we can set up authentication. Add this lines to your .htaccess :

AuthUserFile /www/whatever/git-hosting/.htpasswd
AuthType Basic
AuthName "Git Private"
Require valid-user

and use htpasswd to create a .htpasswd file with the users/hashes you whish.

Now, re-loading config.cgi in your browser should ask you a password.

 Creating an empty Git repository

We’ll have our repositories physically hosted in a subdirectory git-repos of the directory where we did the installation.

Creating an empty Git repository could be as simple as git init, but you may run into permissions issues: the user uploading the files may not be the one running Apache (typically www-data). So, we’ll let a CGI script run this initialization to create the repository with www-data as owner. Create a script init.cgi with this content:

echo 'Content-type: text/plain'

# may require a temporary "chmod 777 ." as normal user.
mkdir -p git-repos/
git init --bare git-repos/example-repo.git/ 2>&1

echo 'Description of my Example' > git-repos/example-repo.git/description

Load it in your web browser, it should create the repository.

When you’re done, it is advised to disable this script (for example by commenting-out its content) for security reasons. Otherwise, someone may run it by accident or malice in the future (the content above should be safe, but it won’t be the day you add an rm -fr ... !).

 Serve the repository using Git over HTTP(S)

We’ll now create a small CGI script that wraps git-http-backend. In theory, you could directly map some URL to git-http-backend, but I like having a little control (i.e. ability to add if/then/else, to export variables, or to do whatever I want before and after serving the repository) over what’s going on with a small script.

In your .htaccess, add the following two lines:

SetEnv GIT_PROJECT_ROOT /www/whatever/git-hosting/git-repos

Create a script git.cgi with this content:

#! /bin/sh

git http-backend "$@"

Now, you should be able to clone your empty Git repository with e.g.

git clone https://example.com/whatever/git-hosting/git.cgi/example-repo.git/

If it doesn’t work, try loading this URL (well, adapted to your case obviously) in your browser:


It should normally show something like ref: refs/heads/master.

If it still doesn’t work, you may add some logging code to your git.cgi, like this:

#! /bin/sh


date >> "$logfile"

git http-backend "$@" 2>> "$logfile" || echo failed >> "$logfile"

echo >> "$logfile"

and/or run git clone with more debug information activated:

GIT_CURL_VERBOSE=1 GIT_TRACE=1 git clone -v http://...

 Installing Gitweb

The simplest solution I found to have a running Gitweb instance is to install it manually. From the gitweb/ subdirectory of Git’s source tree, run this (replacing $INSTALL_PATH with the place where you want to install it):

make GIT=git DESTDIR=$INSTALL_PATH gitwebdir=. bindir=/usr/bin/
make GIT=git DESTDIR=$INSTALL_PATH gitwebdir=. bindir=/usr/bin/ install

This should create a file gitweb.cgi and a static/ directory in your installation directory. Gitweb is relatively lightweight (< 500Ko), so it’s no big problem to copy the whole thing for multiple instances.

Next to gitweb.cgi, create a file gitweb_config.perl with the following content (adapted to your needs):

$projectroot = "/www/whatever/git-hosting/git-repos";

(no trailing-slash on the path)

Now load the URL corresponding to gitweb.cgi, and hopefully get the list of projects :-)

 Running git gc

Git repositories like to be regularly garbage-collected. You can do that with a simple CGI script like this one:

#! /bin/sh

echo 'Content-Type: text/plain'

for d in git-repos/*/; do
    printf "%s ... " "$d"
    (cd "$d" && git gc) && echo "OK" || echo "failed" 

(to be called from your web browser)

Valid XHTML 1.0 Transitional
SPIP | | Site Map | Follow site activity RSS 2.0
Graphic design (c) styleshout under License Creative Commons Attribution 2.5 License