Workshop · back to the section

I fingerprint every file after a deploy

How do I know the server has what I uploaded? Hugging Face answered this with the fingerprints of its packages; the same method works on a website, and it is worth knowing where it is blind.

Written by
Kristóf Karner — independent developer, Budapest
Updated
25 September 2026

After the July 2026 break-in, Hugging Face had to answer one question quickly: had the attackers changed anything the company releases to the public? The answer came from the fingerprints of the released container images and packages: according to the technical timeline, every one of them matched the expected value. The chain of the break-in is covered in another Workshop piece.

On a website, the same question sounds like this: does the server have what I uploaded? The same method answers it; its blind spots are just worth knowing in advance.

What a fingerprint is

A fingerprint, also called a hash, is a short identifier calculated from a file's content, on this site with SHA-256. If a single byte of the file changes, the fingerprint becomes completely different, and in practice no two different files can be made to produce the same fingerprint. So it is enough to compare the two sides' fingerprint lists: where they match, the file is the same, byte for byte.

How I compare on this site

This site is hand-written HTML, and a comparison runs after every deploy. I calculate the fingerprint of every file on my computer, a command does the same on the server, and I compare the two lists sorted by path. Simplified, it looks like this:

Comparison after a deployShell
# on my computer: every file's fingerprint, sorted by path
(cd src && find . -type f -exec shasum -a 256 {} +) | sort -k 2 > local.txt

# the same on the server
ssh server 'cd ~/public_html && find . -type f -exec sha256sum {} +' | sort -k 2 > live.txt

# what is only on one side, or differs
diff local.txt live.txt

# files on the server that are not world-readable: the right answer is 0
ssh server 'find ~/public_html -type f ! -perm -o=r | wc -l'

The difference shows three things: what is only on my side, what is only on the server, and which files differ in content. Files that are on the server on purpose, such as the web server's configuration and the host's own files, are left out; every other difference is an error until I know where it came from.

This comparison has caught my own mistake too. On 25 September 2026 it showed a difference after a deploy, because files in the source were still changing five minutes after packaging: the site worked flawlessly, the server simply held something other than my computer. On 15 August the same mistake had once taken half-finished work live. Since then, a deploy does not even start while the source has uncommitted changes.

What a matching fingerprint does not prove

A matching fingerprint proves that the content is identical, though not that the web server is able to serve the file. An image uploaded from macOS that only its owner can read matches byte for byte on the server, yet the page shows it broken while returning 200. That is why a second step runs after the comparison: it counts the files on the server that are not world-readable and the folders that cannot be entered, and both numbers have to be zero. How permissions travel through an archive is detailed in an earlier Workshop piece.

On WordPress: the official fingerprints

On WordPress, WordPress.org publishes the fingerprints of the core files and of the plugins installed from wordpress.org, and WP-CLI can compare the files on the server against them with two commands:

Core and pluginsWP-CLI
wp core verify-checksums --include-root
wp plugin verify-checksums --all

The first compares WordPress core with the installed version, and with the --include-root option it also warns about files in the root folder that do not belong to WordPress, so a forgotten backup or an injected file turns up too. The second checks every file of the wordpress.org plugins against the official fingerprints (WP-CLI documentation).

The method is blind where there is nothing to compare against. Premium plugins have no public fingerprint list, WP-CLI has no such command for themes, and for uploaded images and documents there cannot be an official list at all. These can be measured against your own known state: fingerprints saved at upload, or the copy on your own computer.

Where fingerprints cannot see: the database

Some WordPress hacks leave the files alone and only write into the database: they add a new admin account, or put spam among the posts. The database changes by itself with every edit, so its fingerprint says nothing. Here the risky parts are worth watching separately: the lists of admin accounts and active plugins. What other signs give a hack away is covered in a piece in Writing.

What I do about it in my own work

On this site, the comparison above runs after every deploy, together with the permission count. On the WordPress sites I maintain, a daily check looks at my own code files: whether they are still on the server, whether they have been emptied, and whether they still contain the part that makes them work. An update or the host's maintenance can remove my own code or undo a fix, while the page keeps loading without a flaw. The same check also compares the list of admin accounts with the expected one.

On one of the sites, the host offers no command line. There my own code writes a marker into the page, and the daily check looks for it from the outside: if the marker disappears, the code is not running.

Questions on this topic

What is a file fingerprint?

A short identifier calculated from a file's content, for example with SHA-256. If a single byte of the file changes, the fingerprint becomes completely different, so comparing the fingerprints of two file lists shows what differs.

How can I check whether WordPress files have been modified?

WP-CLI's wp core verify-checksums command compares WordPress core, and wp plugin verify-checksums --all the plugins installed from wordpress.org, with the official fingerprints. Premium plugins, themes and uploaded files are not covered: those have to be measured against your own known state.

Does a fingerprint show if someone wrote into the database?

No. The database changes with every edit, so its fingerprint gives nothing away. There, the risky parts, such as the lists of admin accounts and active plugins, are worth watching separately.

How often is it worth comparing?

After every deploy, because that is when it turns out whether the upload was complete; and regularly as well, because an update, the host's maintenance or an attacker can change the files after the upload.

← Back to the Workshop