Steps This can also be done purely in MySQL via cron:
30 3 * * * mysql -u user -pPASS dbname \
-e "UPDATE posts SET ip_address=NULL WHERE created_at < (NOW() - INTERVAL 7 DAY)"
If you prefer hashing instead of clearing (sometimes useful for abuse analysis), you can do it with php:
Recommended anonymization strategy (best practice)
IPv4
Example:
192.168.123.45 → 192.168.123.0
IPv6
Example:
2001:0db8:85a3:0000:0000:8a2e:0370:7334
- anonymize to /64, keep first 4 blocks
This is explicitly accepted under GDPR as anonymization (not pseudonymization).
<?php
declare(strict_types=1);
/**
* Flarum: Anonymize post IP addresses older than X days
* IPv4 -> last octet zeroed
* IPv6 -> anonymized to /64
* Run via cron (CLI only)
*/
// ======================
// CONFIG
// ======================
$dbHost = 'localhost';
$dbName = 'your_database';
$dbUser = 'your_db_user';
$dbPass = 'your_db_password';
$days = 7;
$logFile = __DIR__ . '/flarum_ip_anonymizer.log';
// ======================
date_default_timezone_set('UTC');
try {
$pdo = new PDO(
"mysql:host={$dbHost};dbname={$dbName};charset=utf8mb4",
$dbUser,
$dbPass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
/**
* IPv4 anonymization
*/
$ipv4 = $pdo->prepare("
UPDATE posts
SET ip_address = CONCAT(
SUBSTRING_INDEX(ip_address, '.', 3),
'.0'
)
WHERE ip_address IS NOT NULL
AND ip_address REGEXP '^[0-9]{1,3}(\\.[0-9]{1,3}){3}$'
AND created_at < (NOW() - INTERVAL :days DAY)
");
$ipv4->bindValue(':days', $days, PDO::PARAM_INT);
$ipv4->execute();
$ipv4Count = $ipv4->rowCount();
/**
* IPv6 anonymization (/64)
*/
$ipv6 = $pdo->prepare("
UPDATE posts
SET ip_address = CONCAT(
SUBSTRING_INDEX(ip_address, ':', 4),
'::'
)
WHERE ip_address IS NOT NULL
AND ip_address LIKE '%:%'
AND created_at < (NOW() - INTERVAL :days DAY)
");
$ipv6->bindValue(':days', $days, PDO::PARAM_INT);
$ipv6->execute();
$ipv6Count = $ipv6->rowCount();
file_put_contents(
$logFile,
sprintf(
"[%s] IPv4 anonymized: %d | IPv6 anonymized: %d\n",
date('Y-m-d H:i:s'),
$ipv4Count,
$ipv6Count
),
FILE_APPEND
);
} catch (Throwable $e) {
file_put_contents(
$logFile,
sprintf("[%s] ERROR: %s\n", date('Y-m-d H:i:s'), $e->getMessage()),
FILE_APPEND
);
exit(1);
}
exit(0);
Cronjob:
15 3 * * * /usr/bin/php /var/www/scripts/flarum_anonymize_post_ips.php >/dev/null 2>&1
And this script is safe, cron-friendly, and does exactly one thing:
- removes stored IPs after X days:
<?php
declare(strict_types=1);
/**
* Flarum: Remove post IP addresses older than X days
* Sets posts.ip_address to NULL
* Run via cron (CLI only)
*/
// ======================
// CONFIG
// ======================
$dbHost = 'localhost';
$dbName = 'your_database';
$dbUser = 'your_db_user';
$dbPass = 'your_db_password';
$days = 7; // retention period
$logFile = __DIR__ . '/flarum_remove_ips.log';
// ======================
date_default_timezone_set('UTC');
try {
$pdo = new PDO(
"mysql:host={$dbHost};dbname={$dbName};charset=utf8mb4",
$dbUser,
$dbPass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
$stmt = $pdo->prepare("
UPDATE posts
SET ip_address = NULL
WHERE ip_address IS NOT NULL
AND created_at < (NOW() - INTERVAL :days DAY)
");
$stmt->bindValue(':days', $days, PDO::PARAM_INT);
$stmt->execute();
$affected = $stmt->rowCount();
file_put_contents(
$logFile,
sprintf(
"[%s] Removed IPs from %d posts\n",
date('Y-m-d H:i:s'),
$affected
),
FILE_APPEND
);
} catch (Throwable $e) {
file_put_contents(
$logFile,
sprintf("[%s] ERROR: %s\n", date('Y-m-d H:i:s'), $e->getMessage()),
FILE_APPEND
);
exit(1);
}
exit(0);
I have tested this on my Forum, and it works perfectly, you just have to set up Cronjob by yourself:
0 3 * * * /usr/bin/php /var/www/scripts/flarum_remove_post_ips.php >/dev/null 2>&1
Greetings