How to Restore a Website From Backup After Data Loss
To restore website from backup, you put the files back first, then the database, then the configuration that ties them together, and you verify each layer before moving to the next. The order matters because a database import that references a theme directory you have not restored yet will look fine in the admin panel and fail in the browser.
Before you touch anything, stop writes to the site. If the site is still reachable, put it in maintenance mode or block traffic at the web server so users do not create orders, comments or uploads that will vanish when you overwrite the database. If the site is already down, leave it down. A half-restored site serving stale pages is worse than a clean 503, because search engines and customers both react badly to content that flickers in and out.
Find out what you actually lost
Data loss comes in three shapes, and the recovery path differs for each. Files only, which usually means a bad deploy, a deleted uploads directory or a failed plugin update. Database only, which usually means a corrupted table, a botched migration or a dropped schema. Or the whole account, which means you are rebuilding from an archive someone else is holding.
Check the filesystem first. A quick way to see what survived and how old it is:
find /var/www/site -type f -newermt "2024-01-01" -printf '%T+ %p\n' | sort | tail -20
ls -la /var/www/site/wp-content/uploads | head
If the newest files are all from the moment things broke, you are looking at a partial loss. If the directory is empty or missing entirely, treat it as a full account restore. For the database, connect and check that the tables exist and have rows:
mysql -u dbuser -p dbname -e "SHOW TABLES; SELECT COUNT(*) FROM wp_posts;"
An empty result set where you expect rows tells you the schema survived but the data did not, which is a different restore than a missing schema.
Restore the files before the database
Pull the archive down to a scratch directory, not straight into the live path. Unpack it and inspect the top level before you copy anything into place. Backup archives are inconsistent about whether they contain a wrapping folder, and copying one level too deep is the single most common cause of a restore that appears to succeed and produces a blank page.
tar -tzf backup.tar.gz | head -20
tar -xzf backup.tar.gz -C /tmp/restore
Once you can see the structure, move the live document root aside rather than deleting it. Renaming it to something like public_html.broken costs nothing and gives you a fallback if the archive turns out to be older or thinner than you thought. Then copy the restored tree into place and fix ownership, because archives rarely preserve the user your web server runs as. Files should generally be readable by the server and writable only where the application needs it, which for most PHP applications means the uploads directory and any cache directory.
Check permissions on a few representative files before you go further. A config file that is world readable is a security problem you do not want to reintroduce during a recovery.
Restore the database and reconnect it
Import the dump into a database, and import it into a fresh schema rather than over the top of the damaged one. That way a failed import leaves the original untouched and you can retry.
mysql -u dbuser -p -e "CREATE DATABASE dbname_new;"
mysql -u dbuser -p dbname_new < dump.sql
Then look at the application config, which is where most restores quietly fail. The database name, user, password, host and table prefix in your config file all have to match what you just imported. If the backup came from a different server, the credentials in the archive will not work and the site will throw a connection error that looks like corruption but is not. Edit the config, do not guess at it.
After the connection works, check that the site URL stored in the database matches the domain you are serving. Many applications store absolute URLs in the database, so a restore onto a staging hostname or a different scheme will produce redirect loops or assets loaded from the old domain. Fix the stored values with a targeted update rather than a blind search and replace, because serialized data in the database will break if you replace strings of different lengths.
Verify before you reopen
Bring the site up on a temporary hostname or with your own machine pointed at the server through /etc/hosts, and check the response headers rather than just eyeballing the page. A restore that returns content with a 200 is not the same as a restore that is correct.
curl -I https://example.com/
curl -s https://example.com/ | grep -i "site title"
Look for the status code, the Content-Type, and any caching headers your CDN or reverse proxy may still be holding. Then walk the actual user journeys: log in to the admin area, load a page with images, submit a form, and check that a new row lands in the database. Confirm that scheduled tasks and cron entries still point at the right paths, because those are configured outside the backup and are easy to forget.
Finally, compare the restored content against what you expect. A count of posts, pages, products or users from before the incident is a fast sanity check. If the numbers are close but not equal, find out why before you declare victory, because a partial import often fails near the end and leaves you with most of the data and no error message.
What to do next
Once the site is verified, take a fresh backup immediately and store it somewhere other than the server you just recovered. Then write down what you did, in order, while it is still fresh, because the next incident will not be identical but the sequence will be. Test a restore on a spare environment before you need one, and confirm that your backups include the database, the uploads directory and the config files, since archives that omit any one of those three will send you through this process again.
