In short: Backup files in the web root, that is, database dumps (.sql), archives (.zip, .tar.gz) and backup copies (.bak, .old, ~), can be downloaded by anyone who knows or guesses the file name. Search for such files regularly with find, move backups out of the public directory, block typical extensions in the web server and check in the access logs whether they have already been retrieved.
The Backup That Becomes a Leak
Shortly before an update, someone quickly creates a backup: backup.zip in the main directory, next to it datenbank.sql. The update works, the files stay where they are. Months later they are still there, with the complete code, all customer data and the website's credentials.
This pattern is among the most common findings in reviews of web servers. It needs no exploit and no vulnerability in PHP. A web server serves from the web root whatever is there, and archives or SQL files it does not execute but offers for download.
An archive of the website contains not only data but also the code including configuration. Anyone who downloads it can search for vulnerabilities at leisure and knows the database credentials.
Automated scanners search specifically for typical names such as backup.zip, site.tar.gz or dump.sql, often combined with the domain name or a date. Anyone who believes an unusual file name protects them underestimates how systematically this search runs.
Which Files Are Meant
Backup files in the web root come in several forms:
| Type | Typical extensions | What is inside |
|---|---|---|
| Database dumps | .sql, .sql.gz, .dump |
customer data, orders, password hashes, settings |
| Website archives | .zip, .tar, .tar.gz, .tgz, .rar, .7z |
entire code, configuration, uploads |
| Copies of individual files | .bak, .old, .orig, .save, .php~, .copy |
often configuration files with credentials |
| Editor leftovers | .swp, .swo, ~ at the end |
parts of files being edited |
| Plugin backups | dedicated subfolders in the CMS | complete backups, sometimes with guessable names |
Copies of configuration files are especially delicate. The server executes the original file config.php, and its contents stay hidden. The copy config.php.bak it serves as text, complete with database password.
Finding Backups in the Web Root: Searching on the Server
The following search lists suspicious files below the web roots with date and size. Adapt the paths to your server:
find /var/www /srv/www -type f \( \
-iname '*.sql' -o -iname '*.sql.gz' -o -iname '*.dump' -o \
-iname '*.zip' -o -iname '*.tar' -o -iname '*.tar.gz' -o -iname '*.tgz' -o \
-iname '*.rar' -o -iname '*.7z' -o \
-iname '*.bak' -o -iname '*.old' -o -iname '*.orig' -o -iname '*.save' -o \
-iname '*~' -o -iname '*.swp' \) \
-printf '%TY-%Tm-%Td %10s %p\n' 2>/dev/null | sort
Not every match is a problem. Some archives are deliberately provided for download, such as manuals. But every file deserves a conscious decision. Pay particular attention to large files and to folders of backup plugins. With shared hosting without shell access, the file overview in the hosting panel or a retrieval via SFTP helps.
If you find a database dump with customer data in the web root, move it out of the public directory immediately and secure the access logs before they are rotated. Only then clarify whether the file was retrieved.
Has the File Already Been Downloaded?
The answer is in the web server's access logs. What you are looking for are successful retrievals, that is, status 200, of the files found:
# Successful retrievals of typical backup extensions (combined log format)
zgrep -hE '\.(sql|sql\.gz|zip|tar|tgz|gz|bak|old|7z|rar)[ ?]' /var/log/nginx/access.log* \
| awk '$9 == 200 {print $4, $1, $7, $10}'
The output shows time, address, path and size of the response. If the transferred size matches the file size, the file was downloaded completely. How far back the logs go depends on the rotation. If they do not reach back to the file's creation date, an earlier retrieval cannot be ruled out. This is described in detail in Analyzing Web Server Logs After an Attack. Without access to the raw logs, for example with shared hosting, ask the host for the logs for the period in question.
If a dump with personal data was retrieved or this cannot be ruled out, the review of a reporting obligation begins. The basics are summarized in GDPR Reporting Obligation: 72 Hours After the Incident; the assessment in the individual case belongs to your data protection officer. You replace credentials from retrieved files in any case.
Backup Plugins, Panels and Moves
Many finds trace back to backup plugins of CMSs and shops. They often store backups in a subfolder of the web root, sometimes with names made of date and domain that can be guessed. Check three settings in each plugin: where is it backed up to? Are local copies deleted after uploading to an external destination? Is the local folder protected against direct retrieval?
Backups from hosting panels are generally outside the web root, for example in the home directory. It becomes problematic when someone copies such a backup into the web root because downloading it in the browser is more convenient than via SFTP. For moves, the following therefore applies: transfer via SFTP or directly between the servers, never via a public link.
If agencies or service providers look after your websites, this rule should also be in the contract, together with an obligation to clean up after maintenance work. Notes are given in IT Service Provider Contracts: Defining Security Obligations Clearly.
Preventing It for Good: Storage Location and Blocks
To keep the problem from recurring, two levels are needed. The first is organizational: backups have a fixed location outside the web root and also outside the server. What a resilient backup looks like is described in the guide Backups Following the 3-2-1 Rule.
The second level is technical: the web server refuses typical backup extensions across the board, even if a file does get left behind.
location ~* \.(sql|sql\.gz|dump|bak|old|orig|save|swp|swo)$ {
return 404;
}
location ~ ~$ {
return 404;
}
Block archives such as .zip only if your website offers no downloads in this format, or specifically in the directories where no downloads are stored. A regular automatic search with notification and a short rule in the team also help: no backups and copies in the web root, not even for five minutes. Related cases such as hidden configuration files are covered in Protecting Credentials and .env Files Properly.
How does PHP SOS help?
- We search your web roots for dumps, archives, copies and plugin backups and assess every find.
- We use the access logs to check whether files were retrieved, and document the result for your data protection assessment.
- We set up blocks in the web server and a regular automatic search.
- We move backups to a protected location outside the web root.
This work is part of our server hardening for PHP web servers.
Frequently asked questions
Why do backups end up in the web root in the first place?
Usually out of convenience: a dump is created for a move, a plugin stores backups in its own folder, or before a change a copy of a file with the extension .bak is quickly made. Afterwards, the file is forgotten.
How dangerous is a file like config.php.bak?
Very. A file with the extension .php is executed by the server, and its contents stay hidden. A copy with a different extension, by contrast, is usually served as text, complete with database password and keys.
Is a publicly retrievable database dump a data breach?
If it contains personal data and was retrieved, or a retrieval cannot be ruled out, a reporting obligation under GDPR may come into consideration. Your data protection officer or your legal counsel takes over the assessment in the individual case.
Where should backups be stored instead?
Outside the web root and also outside the server, following the 3-2-1 rule. At least one copy should be stored in such a way that the web server itself cannot delete or change it.