Skip to content

Kirby 3.9.8

Cleaning up content files

Kirby doesn't delete fields from content files if you later remove them from your blueprints, rename them, or if you add fields manually with an editor. Especially during development, you therefore often end up with a lot of unused stuff you will probably want to clean up before handing a project over to your client.

This script cleans up your page or file content files. It compares fields in the content files with those defined in the blueprint and removes all undefined fields, no matter if they contain content or not.

You can use the script with a pages or a files collection. By default, the script below runs through the complete pages index. Adapt $collection as required. It is not recommended to run through the complete index of a large site or through thousands of files because you might run into a timeout.

You can also set the fields to ignore in the $ignore array. By default, we ignore the following fields: uuid, for pages title and slug and for files template and sort.

Create a new cleanup.php file in your site root and copy the script code into it.

Then run the script with You can also run the script on the command line or put the code into a route if you cannot call a PHP script in your browser. Delete the script when done.

Before you use this script, make sure you have a backup of your project.

Do not use this script if your project contains fields not defined in blueprints on purpose.


 * Script to clean up unused fields in page or file content files
 * Compares fields present in the content file 
 * with fields defined in the blueprint
 * and removes all undefined fields

require __DIR__ . '/kirby/bootstrap.php';

$kirby = new Kirby;

// Authenticate as almighty

// Define your collection
// Don't use `$site->index()` for thousands of pages
$collection = $kirby->site()->index();

// set the fields to be ignored
$ignore = ['uuid', 'title', 'slug', 'template', 'sort'];

// call the script for all languages if multilang
if ($kirby->multilang() === true) {
    $languages = $kirby->languages();
    foreach ($languages as $language) {
        cleanUp($collection, $ignore, $language->code());
} else {
    cleanUp($collection, $ignore);

function cleanUp($collection, $ignore = null, string $lang = null) {
    foreach($collection as $item) {
        // get all fields in the content file
        $contentFields = $item->content($lang)->fields();

        // unset all fields in the `$ignore` array
        foreach ($ignore as $field) {
            if (array_key_exists($field, $contentFields) === true) {

        // get the keys
        $contentFields = array_keys($contentFields);

        // get all field keys from blueprint
        $blueprintFields = array_keys($item->blueprint()->fields());

        // get all field keys that are in $contentFields but not in $blueprintFields
        $fieldsToBeDeleted = array_diff($contentFields, $blueprintFields);

        // update page only if there are any fields to be deleted
        if (count($fieldsToBeDeleted) > 0) {

            // flip keys and values and set new values to null
            $data = array_map(fn ($value) => null, array_flip($fieldsToBeDeleted));

            // try to update the page with the data
            try {
                $item->update($data, $lang);
                echo Html::tag('p', 'The content file for ' . $item->id() . ' was updated');
            } catch (Exception $e) {
                echo Html::tag('p', $item->id() . ': ' .$e->getMessage());
        } else {
            echo Html::tag('p', 'Nothing to clean up in ' . $item->id());

Instead of echoing the results on screen, you can also write them into a log file if you prefer.