phpMyAdmin is the classic web interface for MySQL and MariaDB: browse tables, run queries, import and export. It’s also one of the most attacked URLs on the internet, so installing it is easy — and securing it is the part that matters.
Do you need it? For occasional admin work, a desktop client (DBeaver, TablePlus, HeidiSQL, MySQL Workbench) over an SSH tunnel is more secure, since nothing web-facing is added. If you or your team need a browser-based tool, follow the steps below.
1. Install
Ubuntu and Debian package phpMyAdmin:
sudo apt update
sudo apt install -y phpmyadmin php-mbstring php-zip php-gd php-json php-curl
The installer asks:
- Web server to configure automatically — choose none (press Tab, then Enter). It offers Apache and lighttpd only; we’ll configure Nginx ourselves.
- Configure database with dbconfig-common — Yes. It creates a
phpmyadmincontrol database and user for advanced features.
Files land in /usr/share/phpmyadmin. The packaged version receives security updates through apt, which is the main reason to prefer it over a manual download.
2. Create a user to log in with
On Ubuntu, MySQL/MariaDB root uses socket authentication and can’t log in through phpMyAdmin. Create a password-based account instead:
CREATE USER 'dbadmin'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON *.* TO 'dbadmin'@'localhost' WITH GRANT OPTION;
Or, better, log in with each application’s own user, which only sees its own database. See MySQL users and privileges.
3. Nginx config with a non-obvious path
Bots probe /phpmyadmin, /pma, /phpMyAdmin and dozens of variants all day. Use a path of your own. Inside an existing HTTPS server block (or a dedicated admin subdomain):
location ^~ /db-a8f3/ {
alias /usr/share/phpmyadmin/;
index index.php;
# Access control — see step 4
allow 198.51.100.25;
deny all;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ ^/db-a8f3/(libraries|setup|templates|locale|sql)/ {
deny all;
}
}
The ^~ modifier stops other regex locations (like the site’s general \.php$ block) from catching these requests, and $request_filename resolves correctly with alias.
sudo nginx -t && sudo systemctl reload nginx
Open https://example.com/db-a8f3/ — over HTTPS only, since you’re sending database passwords.
4. Restrict access (pick at least one)
IP allow-list (above) — the strongest simple option if you have a fixed IP.
HTTP basic auth in front of phpMyAdmin’s own login:
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd-pma alice
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd-pma;
Add these inside the location ^~ /db-a8f3/ block. Bots never reach the phpMyAdmin login page.
SSH tunnel only — bind phpMyAdmin to a server block listening on 127.0.0.1:8081 and reach it with ssh -L 8081:127.0.0.1:8081 user@server. Nothing is exposed publicly.
5. Harden phpMyAdmin’s config
Edit /etc/phpmyadmin/config.inc.php (or add a file in /etc/phpmyadmin/conf.d/):
$cfg['Servers'][$i]['AllowRoot'] = false; // no root logins
$cfg['LoginCookieValidity'] = 1800; // 30-minute sessions
The Debian package generates the blowfish_secret used to encrypt cookies automatically.
6. Larger imports
Import size is limited by PHP’s upload_max_filesize/post_max_size and Nginx’s client_max_body_size. For large SQL files, skip the browser entirely and import from the shell — it’s faster and has no limits:
mysql -u dbadmin -p shop < dump.sql
7. Keep it updated
Updates come with your regular sudo apt upgrade. If you installed phpMyAdmin manually from a tarball, you must update it manually too — a frequently forgotten task that leaves vulnerable versions online for years.
Troubleshooting
#1698 - Access denied for user 'root'@'localhost'— root uses socket auth; log in with the user from step 2.- Page downloads instead of running — PHP isn’t being passed to PHP-FPM; check the nested
\.php$block and the socket path. - 404 on the login page — the
aliaspath or trailing slashes don’t match; both the location and the alias should end with/. The configuration file now needs a secret passphrase— set$cfg['blowfish_secret']to a random 32-character string.- Login loops back to the login page — session directory not writable or mixed HTTP/HTTPS; serve it only over HTTPS.
Alternatives
- Adminer — a single PHP file, lighter than phpMyAdmin, supports MySQL and PostgreSQL. See installing Adminer.
- pgAdmin for PostgreSQL — see installing pgAdmin.
New database server? Start with installing MySQL on Ubuntu.