My PHP configuration file got exposed. What should I do?
PHP configuration files — config.php, db.php, database.php, settings.php — typically store database credentials, API keys, and application secrets as PHP variables. When these files are served as plain text instead of being executed by PHP, every credential inside is readable to anyone who requests the URL.
PHP files should never be served as plain text — this is a server misconfiguration.
Normally, PHP files are executed by the PHP interpreter and the output (HTML, JSON, etc.) is sent to the browser — the source code is never visible. If the raw PHP source was served, your web server's PHP handler is not configured for that file extension or path. Fix the misconfiguration first, then rotate credentials.
// the 60-second version
- Fix the PHP handler misconfiguration so
.phpfiles are executed, not served as text. - Change the database password and rotate every other credential in the file.
- Move config files above the web root so they can never be directly requested.
- Check access logs for the exposure window and investigate suspicious activity.
01Understand what your PHP config file contains
PHP configuration files vary in structure, but they almost universally contain database connection credentials as PHP variables:
// Database credentials — fully exposed as plain text define('DB_HOST', 'localhost'); define('DB_USER', 'webapp'); define('DB_PASS', 'hunter2'); define('DB_NAME', 'production_db'); // API keys — also exposed define('STRIPE_SECRET_KEY', 'sk_live_...'); define('SENDGRID_API_KEY', 'SG...');
Beyond the obvious credentials, also look for: salts for password hashing (if these are exposed, rainbow tables can be precomputed for your specific salt), admin panel URLs or secret tokens used to protect admin access, and any OAuth client secrets for third-party integrations.
If your database port is publicly reachable (MySQL on port 3306, PostgreSQL on 5432), the exposed credentials may allow direct database access from the internet — not just through your application. Check your firewall rules immediately.
02Block access and fix the PHP handler
First, determine why PHP source is being served as text. The most common causes are a missing or broken fastcgi_pass directive in Nginx, or PHP-FPM not running. Check whether PHP is executing at all:
# check if PHP-FPM is running systemctl status php8.2-fpm # check nginx config has PHP handling grep -n "fastcgi_pass\|\.php" /etc/nginx/sites-enabled/default
A correct Nginx PHP handler block looks like this:
location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; }
While fixing the PHP handler, also add an explicit deny rule for config file names as a belt-and-suspenders measure:
location ~* (config|database|db|settings|credentials)\.php$ { deny all; return 404; }
03Change the database password immediately
Connect to the database as an administrative user and change the application user's password:
-- connect as root ALTER USER 'webapp'@'localhost' IDENTIFIED BY 'new-strong-random-password'; FLUSH PRIVILEGES; -- also restrict to only allow local connections if not already -- (check with: SELECT Host, User FROM mysql.user;)
Then update the config file with the new password, and restart your application to confirm it still connects. Also revoke and replace any API keys present in the file through each provider's dashboard.
04Move config files above the web root
The safest long-term fix is to store configuration files in a directory that the web server cannot serve. Place them one level above /var/www/html/:
# move config to a directory above the web root
mkdir -p /var/www/config
mv /var/www/html/config.php /var/www/config/config.php
chmod 640 /var/www/config/config.php
chown www-data:www-data /var/www/config/config.php
// In your application entry point: require_once dirname(__DIR__) . '/config/config.php'; // Or use an absolute path: require_once '/var/www/config/config.php';
The web server can only serve files inside the document root (e.g., /var/www/html/). A file at /var/www/config/ is readable by the PHP process (which runs as www-data) but cannot be requested via HTTP.
05Review access logs and check for exploitation
Determine the exposure window and look for suspicious activity in that period:
# find 200 responses for config file requests grep -E "(config|database|db|settings)\.php" /var/log/nginx/access.log \ | grep " 200 " # check MySQL general log for unusual queries (if enabled) tail -100 /var/log/mysql/mysql.log
If the database port was publicly accessible and credentials were exposed, also check your database logs for connections from external IP addresses. Any legitimate connections from your application server should come from localhost or a known internal IP.
Consider switching from PHP define constants to loading credentials from environment variables using $_ENV['DB_PASS'] or getenv('DB_PASS'). Set these in your web server config or a .env file loaded via a library like PHP dotenv — keeping the actual values out of any file that lives in the repository.
Was this guide useful?
These playbooks are free to read and share. If a heads-up ever saved you a bad week, you can say thanks — or jump into the other guides.