# Public .git Folder: An Underestimated Data Leak

> Is your .git folder public? Here is how to check, block access, change your deployment and handle credentials from the history.

Source: https://phpsos.de/en/blog/git-ordner-oeffentlich/  
As of: 25/10/2026  
Provider: PHP SOS, PHP emergency help and server security. Immediate help via WhatsApp: https://wa.me/491736494914?text=Hello%20PHP%20SOS%2C%20we%20need%20help%20with%20our%20PHP%20server.%20Company%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**In short:** A public .git folder arises when a website is rolled out directly in the web root with `git clone` or `git pull` and the web server does not block hidden folders. The complete source code, including its version history and any credentials committed early on, can then be retrieved. Check your domains with a simple request, block access immediately, switch the deployment to packages without `.git` and replace credentials from the history.

## The Source Code to Take Away

Git is the standard tool for version control, and for small teams it is natural to deploy with it too: connect to the server, `git pull`, done. That works reliably and is fast. But it has a side effect that often nobody notices.

The project folder contains a hidden folder `.git`. It holds not only the current state but every file in every version that was ever committed, plus commit messages, names and email addresses of developers, and references to servers. If this folder sits in the web root and the web server serves it, anyone can retrieve it.

For attackers, this is a gift. With the source code, vulnerabilities can be searched for specifically instead of being guessed from outside. And the history often contains things that seemed long deleted: a database password from the early days, an API key, a test file with customer data.

## Is Your .git Folder Public? How to Check

The check takes seconds and changes nothing. For every domain and subdomain, request a file that exists in every Git repository:

```bash
# Expected: 403 or 404
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.git/HEAD
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.git/config
# On the server: are there .git folders below the web roots?
find /var/www /srv/www -maxdepth 4 -type d -name ".git" 2>/dev/null
```

A status of 200 means the folder is reachable. Also pay attention to subdirectories. Sometimes the repository is not in the web root itself but in a plugin or theme folder that is maintained separately with Git.

Also check staging and test environments. They are updated with Git more often than production and are often reachable under guessable subdomains.

## Immediately: Block Access

Until the deployment is switched over, a rule in the web server blocks access to version control folders. A general block for hidden files, as described in [Protecting Credentials and .env Files Properly](https://phpsos.de/en/blog/env-dateien-schuetzen/), covers this too. Anyone who wants to block only version control specifically uses a narrower rule:

```nginx
location ~ /\.(git|svn|hg)(/|$) {
    return 404;
}
```

```apache
RedirectMatch 404 /\.(git|svn|hg)(/|$)
```

Then repeat the check from above, with a real request from outside: a successful configuration test says only that the syntax is correct, not that the rule takes effect before other rules for static files. With panels such as Plesk or cPanel, enter the rule in the additional directives provided for this so that it is not lost the next time the configuration is generated.

> **Act now:**
> If the folder was reachable, treat all credentials that were ever in the repository as known. Blocking alone is then not enough; the values must be replaced.

## Permanently: Roll Out Without .git in the Web Root

The block is a safeguard, not a solution. In the long term, version control does not belong on the production server. Proven approaches, depending on your environment:

| Method | How it works | Suitable for |
|---|---|---|
| Build package from CI | Pipeline builds an archive without `.git`, the server unpacks it into a new release directory | Teams with CI/CD |
| `git archive` | creates an archive of a state without version data | small projects without a pipeline |
| `rsync` with exclusions | transfers only the necessary files | classic servers |
| Container image | Code is copied into the image at build time, `.git` excluded via `.dockerignore` | Container operation |

A release directory per version with a symbolic link to the current state has a further advantage: you can switch back immediately if there are problems. For container setups, [CI/CD: Rebuilding PHP Images Automatically](https://phpsos.de/en/blog/php-images-in-ci-neu-bauen/) describes how builds become reproducible. There you should also check that `.git` does not end up in the image.

```bash
# Example: create a package of a state without version data
git archive --format=tar.gz -o release.tar.gz HEAD
```

## Other Project Files That Do Not Belong in the Web Root

The `.git` folder is the best-known example but not the only one. Anyone who rolls out entire project directories brings further files into the web root that have no business being there:

- Folders of development environments such as `.idea` or `.vscode`, in which extensions for file upload sometimes store server addresses and credentials.
- `composer.json` and `composer.lock`, which reveal the exact versions of all libraries and thus make it easier to search for known vulnerabilities.
- Configurations for tests, build tools and pipelines that give away internal hostnames and processes.
- Operating system leftovers such as `.DS_Store`, which contain file lists of entire directories.

The solution is the same as for the `.git` folder: serve only what has to be public, ideally via a dedicated subfolder as the web root. Anyone who is switching the deployment to packages anyway defines a list of excluded files at the same time and reviews it with every new tool in the project.

## If the Folder Was Open: Checking History and Logs

If it turns out that the `.git` folder was reachable, proceed in this order:

1. **Block access** and repeat the check.
2. **Search the history** for which secrets were ever committed: configuration files, `.env`, keys, database dumps.
3. **Replace credentials**, namely all those found in step 2, even if they are no longer in the code today.
4. **Evaluate access logs** to see whether files under `/.git/` were retrieved with status 200 and from which addresses. How to do this is shown in [Analyzing Web Server Logs After an Attack](https://phpsos.de/en/ratgeber/logfiles-auswerten/).
5. **Review the source code specifically**, because attackers now know it. Known weaknesses should be fixed with priority.
6. **Clarify data protection** if personal data was in the history, such as test exports. Your data protection officer takes over the assessment.

Tools for searching for secrets in repositories belong permanently in the pipeline afterwards. OWASP expressly recommends such checks, as a hook before committing and in CI.

## How does PHP SOS help?

- We check all your domains and subdomains for reachable version control folders.
- We block access immediately and switch the deployment to packages without version data.
- We search the history for secrets and accompany the replacement of the affected credentials.
- We evaluate the access logs and document whether and when data was retrieved.

These measures are part of our [server hardening for PHP web servers](https://phpsos.de/en/leistungen/server-haertung/).


## Frequently asked questions

**How does a public .git folder come about?**  
Usually through a deployment with git clone or git pull directly in the web root. The .git folder with the entire version history then sits next to index.php, and without a block, the web server serves its files.

**Is it enough to delete the .git folder?**  
For the future yes, for the past no. If the folder was reachable, its contents may already have been copied. You should therefore replace credentials that were ever in the repository.

**Does it help to remove credentials from the Git history afterwards?**  
It prevents new copies from containing them. Anyone who has already retrieved the repository still holds the old states. What matters is therefore replacing the credentials, not just rewriting the history.

**Does this affect other version control systems too?**  
Yes. Folders such as .svn or .hg also contain project data and should never be reachable in the web root. A general block for files and folders starting with a dot covers all these cases.

## Sources

- [OWASP Secrets Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html)
- [Nginx documentation](https://nginx.org/en/docs/)
- [Apache HTTP Server: documentation](https://httpd.apache.org/docs/)
