This guide explains how to configure a Git repository to be served anonymously over HTTP using Nginx and git-http-backend. This setup allows users to clone and fetch repositories without authentication, and optionally push if configured. It’s ideal for public repositories or simple internal use cases.
- A Linux server with:
- Git installed (
sudo apt install git) - Nginx installed (
sudo apt install nginx) fcgiwrapinstalled (sudo apt install fcgiwrap) to bridge Nginx andgit-http-backend
- Git installed (
- Basic familiarity with command-line tools and Nginx configuration
-
Create a Repository Directory: Choose a directory to store your Git repositories. For example:
sudo mkdir -p /srv/git sudo chmod -R 755 /srv/git
-
Initialize a Bare Repository: Create a bare Git repository (one without a working directory, suitable for remote access):
cd /srv/git git init --bare myrepo.git -
Enable Anonymous Access: By default,
git-http-backendrequires repositories to be explicitly marked as exportable. Create an empty file namedgit-daemon-export-okin the repository:touch myrepo.git/git-daemon-export-ok
Alternatively, you can configure Nginx to bypass this requirement (see Step 3).
-
Adjust Permissions: Ensure the web server user (typically
www-dataon Debian-based systems) has read access to the repository:sudo chown -R www-data:www-data /srv/git/myrepo.git sudo chmod -R g+rX /srv/git/myrepo.git
If the repository is owned by another user (e.g., your account), see Step 5 for handling ownership mismatches.
fcgiwrap acts as a FastCGI wrapper to run git-http-backend with Nginx.
-
Verify
fcgiwrapis Running: After installation, ensure thefcgiwrapservice is active:sudo systemctl start fcgiwrap sudo systemctl enable fcgiwrapCheck the socket:
ls -l /var/run/fcgiwrap.socket
It should be owned by
www-dataor accessible by the Nginx user. -
Adjust Permissions (if needed): If Nginx can’t access the socket:
sudo chown www-data:www-data /var/run/fcgiwrap.socket sudo chmod g+w /var/run/fcgiwrap.socket
Edit your Nginx site configuration (e.g., /etc/nginx/sites-available/default) to serve Git repositories.
-
Basic Configuration: Add a
serverblock to handle HTTP requests:server { listen 80; server_name your-domain.com; # Replace with your domain or IP # Define the root directory for non-Git content (optional) root /var/www/html; index index.html; # Directory for Git repositories set $git_root /srv/git; # Serve Git repositories under /git/ location ~ ^/git/([^/]+\.git)(/.*)?$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; fastcgi_param GIT_PROJECT_ROOT $git_root; fastcgi_param GIT_HTTP_EXPORT_ALL "1"; # Allow access without git-daemon-export-ok fastcgi_param PATH_INFO /$1$2; # Repo name + subpath (e.g., /myrepo.git/info/refs) fastcgi_param REQUEST_METHOD $request_method; fastcgi_param QUERY_STRING $query_string; fastcgi_pass unix:/var/run/fcgiwrap.socket; } }
-
Key Settings Explained:
GIT_PROJECT_ROOT: Points to the parent directory of your repositories (/srv/git).GIT_HTTP_EXPORT_ALL "1": Allows anonymous access to all repositories under$git_root, bypassing the need forgit-daemon-export-ok.PATH_INFO /$1$2: Captures the repository name (e.g.,myrepo.git) and subpath (e.g.,/info/refs), ensuringgit-http-backendprocesses requests correctly.
-
Test and Reload Nginx:
sudo nginx -t sudo systemctl reload nginx
-
Clone the Repository: From another machine or locally:
git clone http://your-domain.com/git/myrepo.git
This should succeed without authentication.
-
Verify with curl: Check the Git protocol response:
curl -v "http://your-domain.com/git/myrepo.git/info/refs?service=git-upload-pack"Expect a
200 OKresponse with Git protocol data (e.g.,001e# service=git-upload-pack).
If the repository files are owned by a user other than www-data (e.g., your personal account), Git might refuse to operate due to security restrictions introduced in Git 2.35.2+. You’ll see errors like fatal: unsafe repository.
To fix this globally:
sudo git config --system --add safe.directory '*'- This adds
safe.directory = *to/etc/gitconfig, trusting all directories for all users. - Caution: This is a broad setting; for better security, specify the exact path:
sudo git config --system --add safe.directory /srv/git/myrepo.git
Alternatively, change ownership to www-data (as in Step 1) to avoid this step.
By default, this setup only allows fetching/cloning. To enable anonymous pushing:
-
Add Push Support in Nginx: Update the
locationblock:location ~ ^/git/([^/]+\.git)(/.*)?$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; fastcgi_param GIT_PROJECT_ROOT $git_root; fastcgi_param GIT_HTTP_EXPORT_ALL "1"; fastcgi_param PATH_INFO /$1$2; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param QUERY_STRING $query_string; fastcgi_param GIT_HTTP_RECEIVE_PACK "1"; # Enable pushing fastcgi_pass unix:/var/run/fcgiwrap.socket; }
-
Grant Write Permissions: Ensure
www-datacan write to the repository:sudo chown -R www-data:www-data /srv/git/myrepo.git sudo chmod -R g+rwX /srv/git/myrepo.git
-
Test Pushing:
cd myrepo echo "test" > test.txt git add test.txt git commit -m "Test push" git push origin main
Note: Anonymous pushing is insecure for public servers. Consider adding authentication (see below) if this is a concern.
To browse the /git/ directory (e.g., list all repositories):
-
Add a Specific Location: Update the Nginx config:
server { listen 80; server_name your-domain.com; root /var/www/html; index index.html; set $git_root /srv/git; # Directory listing for /git/ location = /git/ { root /srv; # Points to /srv/git/ autoindex on; autoindex_exact_size off; autoindex_localtime on; } # Git repository handling location ~ ^/git/([^/]+\.git)(/.*)?$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; fastcgi_param GIT_PROJECT_ROOT $git_root; fastcgi_param GIT_HTTP_EXPORT_ALL "1"; fastcgi_param PATH_INFO /$1$2; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param QUERY_STRING $query_string; fastcgi_pass unix:/var/run/fcgiwrap.socket; } }
-
Test Browsing:
curl -v "http://your-domain.com/git/"You should see an HTML directory listing of
/srv/git/.
For controlled access (e.g., restrict pushing):
-
Create an
.htpasswdFile:sudo htpasswd -c /etc/nginx/.htpasswd gituser
-
Add Authentication to Nginx:
location ~ ^/git/([^/]+\.git)(/.*)?$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; fastcgi_param GIT_PROJECT_ROOT $git_root; fastcgi_param GIT_HTTP_EXPORT_ALL "1"; fastcgi_param PATH_INFO /$1$2; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param QUERY_STRING $query_string; fastcgi_param GIT_HTTP_RECEIVE_PACK "1"; fastcgi_pass unix:/var/run/fcgiwrap.socket; auth_basic "Git Access"; auth_basic_user_file /etc/nginx/.htpasswd; }
-
Test with Credentials:
git clone http://gituser:password@your-domain.com/git/myrepo.git
- 500 Internal Server Error: Check
/var/log/nginx/error.logfor details (e.g., permissions,fcgiwrapissues). - "unsafe repository": Ensure
safe.directoryis set or ownership matcheswww-data. - "Service not enabled": Verify
GIT_HTTP_RECEIVE_PACKfor pushing. - "aliased" Error: Ensure
PATH_INFOstarts with a slash and matches the repo path.
With this setup, you’ve got a fully functional anonymous Git server over HTTP using Nginx! Adjust paths and settings as needed for your environment.