How to Recover a Site When the Backup Is Corrupt
A corrupt backup is a file that no longer matches the format its restore tool expects, and the recovery path depends on whether the corruption is structural (a broken header, a truncated stream) or content-level (bad rows, mangled files). Start by proving the damage and locating where it begins, because a backup that fails at byte 40 million may still contain almost everything you need.
Confirm the corruption before you touch anything
Copy the archive to a scratch disk first. Every tool you run against a suspect file risks changing its mtime or, worse, rewriting it in place. Work on a copy and keep the original read-only with chmod a-w.
For a gzip-compressed SQL dump, the test is cheap and tells you exactly where the stream breaks:
$ gzip -t site-backup.sql.gz
gzip: site-backup.sql.gz: unexpected end of file
$ gzip -dc site-backup.sql.gz > /dev/null
gzip: site-backup.sql.gz: invalid compressed data--crc error
A CRC error late in the stream means the deflate blocks before that point are intact. A failure in the first few kilobytes means the gzip header itself is damaged and you should look at the raw bytes before giving up:
$ xxd -l 32 site-backup.sql.gz
00000000: 1f8b 0800 0000 0000 0003 7369 7465 2d64 ..........site-d
The magic bytes 1f 8b should be the first two. If they are not, someone has either truncated the file from the front or the transfer was interrupted. For tar archives the equivalent check is tar -tzf archive.tar.gz, which lists entries until it hits the bad block and then reports Unexpected EOF or gzip: stdin: invalid compressed data. For a raw MySQL dump, look for the trailer line -- Dump completed; its absence is your first clue that the dump was cut short.
Corrupt backup recovery website: salvage the readable portion
Once you know roughly where the damage starts, extract everything before it. For gzip, zcat will emit the good blocks and then fail; redirect stdout to a file and ignore the exit status, because the partial output is what you want:
$ zcat site-backup.sql.gz > partial.sql 2> gzip-errors.log
$ tail -n 5 partial.sql
INSERT INTO wp_options VALUES (9921,'siteurl','https://example.com');
The last line will usually be a half-written statement. Open the file in an editor and cut back to the last complete semicolon, then run mysql --force against a fresh database so a bad statement does not abort the whole import. The --force flag makes the client continue past errors, which is exactly what you want when you are importing a recovered fragment and expect a few broken rows.
For tar archives, tar -xzf archive.tar.gz --ignore-zeros skips past zero-filled blocks and often recovers files stored after the damaged region. Extract into an empty directory and compare the file list against what tar -tzf reported before the failure. Anything extracted is usable; anything missing has to come from elsewhere.
If the archive is a database dump with a damaged middle, use sed or a small script to split it on statement boundaries and import the pieces individually. A dump is just text, and a text file with a hole in it is usually 90 percent recoverable by hand.
Rebuild from partial files and database fragments
When no clean copy exists, treat the wreckage as a jigsaw. The filesystem side is the easy part: a tar that extracted 80 percent of wp-content/uploads gives you most images, and the rest can be regenerated from thumbnails or pulled from a CDN cache if you have one. The database is harder, because a partial INSERT is worthless and a partial table is only useful if you can infer its schema.
For MySQL, get the schema first. If the dump contains CREATE TABLE statements before the damage point, you have the structure even when the data is missing. Import the schema into an empty database, then import the salvaged data rows with --force and check which tables are short:
SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema = 'recovered';
Compare those counts against what you expect from the site's behaviour. A wp_posts table with half its rows is still a working site; a wp_users table with none is not, and you will need to recreate accounts manually and force password resets. For a config file, recover the wp-config.php or equivalent from the file archive and check that DB_NAME, DB_USER and the salts are present. If the salts are gone, regenerate them with wp config shuffle-salts or an equivalent tool, which logs everyone out but does not lose data.
For static sites, a partial file tree is usually enough: the HTML pages are independent, and a missing page can be rebuilt from a search engine cache or from the sitemap. Check robots.txt and the sitemap for a list of URLs that should exist, then diff that list against what you recovered.
Prevent the next corrupt backup
Two habits make this scenario rare. First, verify every backup as part of the job that creates it: run gzip -t or mysqlcheck on the restored copy, not just the archive, and fail the job loudly if the check fails. Second, keep at least one backup in a format you can read without the original tool, such as a plain SQL dump or an uncompressed tar, so a broken header does not take the whole copy with it.
Next, take the partial database you just recovered, restore it to a staging host, and click through the site as a user would. Note every broken page, missing image and failed login, because that list is your rebuild plan. Then fix the backup job that produced the corrupt file before you trust it with the next copy, since the same failure will otherwise happen again.
