Cookie authentication keeps database passwords out of phpMyAdmin's server-side configuration, but the encrypted login cookie still needs a stable key. A unique blowfish_secret lets the installation decrypt those credentials across requests without falling back to a session-only key.
The directive retains its historical name, although phpMyAdmin 5.2 and later use Sodium secretbox encryption rather than Blowfish. The key must resolve to exactly 32 bytes; 32 random bytes become 64 characters when represented as hexadecimal text and return to binary form through sodium_hex2bin().
Use the override file loaded by the current installation rather than a sample or package-owned default. Replacing an existing key invalidates cookies encrypted with the old value, so authenticated users must sign in again after the change.
$ export PMA_ROOT=/var/www/html
The official Docker image uses /var/www/html; package and source installations may use another directory.
Related: How to find phpMyAdmin configuration files
$ export PMA_CONFIG=/etc/phpmyadmin/config.user.inc.php
$ sudo install -d -m 0700 -o root -g root /var/backups/phpmyadmin
$ sudo install -m 0600 -o root -g root "$PMA_CONFIG" /var/backups/phpmyadmin/config.inc.php.before-blowfish-secret
Replacing the current secret signs out users whose cookies were encrypted with the old key. The protected copy preserves the previous value for rollback.
$ php -r 'echo bin2hex(random_bytes(32)), PHP_EOL;'
The 64-character output is a credential. Values printed in documentation or transcripts, or shared with another installation, no longer belong to one protected phpMyAdmin instance.
$ sudoedit "$PMA_CONFIG"
$cfg['blowfish_secret'] = sodium_hex2bin('<64-character-hex-value>');
The placeholder denotes the 64 hexadecimal characters produced by random_bytes().
$ sudo php -l "$PMA_CONFIG" No syntax errors detected in /etc/phpmyadmin/config.user.inc.php
$ sudo php -r 'define("PHPMYADMIN", true); define("ROOT_PATH", rtrim($argv[1], "/") . "/"); $vendor = require ROOT_PATH . "libraries/vendor_config.php"; require $vendor["autoloadFile"]; $active = new PhpMyAdmin\Config($vendor["configFile"]); $cfg = []; include $argv[2]; $secret = $cfg["blowfish_secret"] ?? ""; if (strlen($secret) !== 32 || ! hash_equals($secret, $active->get("blowfish_secret"))) { fwrite(STDERR, "active secret mismatch\n"); exit(1); } echo "active secret: 32 bytes\n";' "$PMA_ROOT" "$PMA_CONFIG" active secret: 32 bytes
The check exits nonzero when the selected file is not loaded, a later layer overrides it, or the configured value is not exactly 32 bytes.