Protecting WordPress Database Credentials
Your database credentials are the keys to your WordPress kingdom. Learn how to protect wp-config.php, use environment variables, and secure database access.
Your WordPress database contains everything—posts, users, settings, and sensitive data. The credentials in wp-config.php are the keys to this kingdom. Protecting these credentials is one of the most important security measures you can take.
Database Credential Risks
- wp-config.php exposure - Server misconfiguration
- Backup file leaks - Credentials in backup files
- Version control exposure - Committed to repositories
- Shared hosting risks - Other users on same server
- Error message disclosure - Credentials in error logs
Securing wp-config.php
Move wp-config.php
// WordPress automatically checks one directory up
// Move wp-config.php above web root
// From: /var/www/html/wp-config.php
// To: /var/www/wp-config.php
// WordPress will find it automatically
// File is now inaccessible via web browser
Protect via .htaccess
# Deny access to wp-config.php
order allow,deny
deny from all
# Block access to all sensitive files
Order deny,allow
Deny from all
Nginx Protection
# Nginx configuration
location ~* wp-config.php {
deny all;
}
location ~* /.ht {
deny all;
}
Environment Variables
Use Environment Variables Instead
// In wp-config.php - read from environment
define('DB_NAME', getenv('WP_DB_NAME'));
define('DB_USER', getenv('WP_DB_USER'));
define('DB_PASSWORD', getenv('WP_DB_PASSWORD'));
define('DB_HOST', getenv('WP_DB_HOST') ?: 'localhost');
// Security keys from environment
define('AUTH_KEY', getenv('WP_AUTH_KEY'));
define('SECURE_AUTH_KEY', getenv('WP_SECURE_AUTH_KEY'));
define('LOGGED_IN_KEY', getenv('WP_LOGGED_IN_KEY'));
define('NONCE_KEY', getenv('WP_NONCE_KEY'));
// Set environment variables in server config or .env file
// Apache: SetEnv WP_DB_NAME "database_name"
// Nginx: fastcgi_param WP_DB_NAME "database_name";
Using .env Files (with Dotenv)
// Install php-dotenv via Composer
// composer require vlucas/phpdotenv
// In wp-config.php (above web root)
require_once __DIR__ . '/vendor/autoload.php';
$dotenv = DotenvDotenv::createImmutable(__DIR__);
$dotenv->load();
define('DB_NAME', $_ENV['DB_NAME']);
define('DB_USER', $_ENV['DB_USER']);
define('DB_PASSWORD', $_ENV['DB_PASSWORD']);
define('DB_HOST', $_ENV['DB_HOST']);
// .env file (never commit to version control!)
// DB_NAME=wordpress_db
// DB_USER=wordpress_user
// DB_PASSWORD=complex_password_here
// DB_HOST=localhost
Database User Security
Limit Database Privileges
-- Create dedicated WordPress user with minimal privileges
CREATE USER 'wordpress_user'@'localhost' IDENTIFIED BY 'strong_password';
-- Grant only necessary privileges
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER
ON wordpress_db.*
TO 'wordpress_user'@'localhost';
-- Explicitly deny dangerous privileges
-- FILE, PROCESS, SUPER, SHUTDOWN are never needed
-- For tighter security (restricts some plugin functionality)
GRANT SELECT, INSERT, UPDATE, DELETE
ON wordpress_db.*
TO 'wordpress_limited'@'localhost';
FLUSH PRIVILEGES;
Separate Read/Write Users
// Advanced: Use different users for read/write
// Requires custom code or plugin
// Read-only user for front-end
define('DB_USER_READ', getenv('WP_DB_USER_READ'));
define('DB_PASS_READ', getenv('WP_DB_PASS_READ'));
// Full access user for admin
define('DB_USER_WRITE', getenv('WP_DB_USER_WRITE'));
define('DB_PASS_WRITE', getenv('WP_DB_PASS_WRITE'));
// Switch based on context
if (is_admin() || is_user_logged_in()) {
define('DB_USER', DB_USER_WRITE);
define('DB_PASSWORD', DB_PASS_WRITE);
} else {
define('DB_USER', DB_USER_READ);
define('DB_PASSWORD', DB_PASS_READ);
}
Connection Security
Force SSL Database Connections
// In wp-config.php
define('MYSQL_CLIENT_FLAGS', MYSQLI_CLIENT_SSL);
// Or more detailed SSL configuration
define('MYSQL_SSL_KEY', '/path/to/client-key.pem');
define('MYSQL_SSL_CERT', '/path/to/client-cert.pem');
define('MYSQL_SSL_CA', '/path/to/ca-cert.pem');
Network-Level Database Security
// MySQL bind to localhost only
// In my.cnf or mysqld.cnf
// bind-address = 127.0.0.1
// Or use Unix socket instead of TCP
define('DB_HOST', 'localhost:/var/run/mysqld/mysqld.sock');
Credential Rotation
// Regular credential rotation process
// 1. Generate new strong password
// 2. Update MySQL user password
// 3. Update wp-config.php or environment variable
// 4. Verify site functionality
// 5. Document change date
// Strong password generator
function generate_db_password() {
return bin2hex(random_bytes(24)); // 48 character password
}
Security Checklist
- [ ] wp-config.php protected from web access
- [ ] Database credentials in environment variables
- [ ] Dedicated database user with minimal privileges
- [ ] Database bound to localhost only
- [ ] Regular credential rotation scheduled
- [ ] Credentials excluded from version control
- [ ] SSL connection for remote databases
Conclusion
Protecting database credentials requires multiple layers—file protection, environment variables, privilege restriction, and connection security. Never expose credentials in web-accessible files or version control.
Written by Sarah Chen
WP Folder Shield Team