🛡️ Advanced hardening: isolating wp-config.php outside web root
wp-config.php is the core trust file of a WordPress installation. It stores security keys and database credentials. Keeping it inside public web root (public_html or www) increases risk: if webserver parsing fails, its content may be exposed as plain text.
This guide documents the exact production workflow I use to isolate this file safely, with validation and rollback.
---
1. Configuration isolation concept
The strategy is to move the file one level above web root. If your public root is /home/accountuser/public_html/, move wp-config.php to /home/accountuser/.
Because this parent path is not served over HTTP/HTTPS, direct web access is blocked by design.
Immediate technical gains
- Lower exposure to automated recon.
- Extra protection against accidental file disclosure.
- Cleaner separation between public code and secrets.
---
2. Native WordPress relocation method
WordPress supports this natively: if wp-config.php is missing in web root, it checks the parent directory.
Operational procedure
- Full backup
- Files backup.
- Database dump.
- Server access
- SSH or FTP.
- WordPress root confirmation
- Verify
wp-adminandwp-includes.
- Move file
cd /home/accountuser/
cp public_html/wp-config.php public_html/wp-config.php.bak.$(date +%F-%H%M)
mv public_html/wp-config.php ./wp-config.php
- Syntax validation
php -l /home/accountuser/wp-config.php
- Functional validation
- Open home page.
- Access
/wp-admin. - Publish/update a test post.
In standard layouts, no index.php editing is required.
---
3. Custom path method (stub loader)
If you want a specific secure path (for example, /home/accountuser/config/wp-config.php), use a lightweight root stub.
Recommended flow
- Move real config:
mkdir -p /home/accountuser/config
mv /home/accountuser/public_html/wp-config.php /home/accountuser/config/wp-config.php
- Create minimal
public_html/wp-config.php:
<?php
/** Load config from a secure directory */
require_once('/home/accountuser/config/wp-config.php');
This avoids editing index.php and lowers upgrade breakage risk.
Stub security checklist
- Correct absolute path in
require_once. - Real config outside public directory.
- App and admin path tested after change.
---
4. OS-level hardening with file permissions
Relocation alone is insufficient if permissions remain weak.
Recommended permissions
Most restrictive mode:
chmod 400 /home/accountuser/config/wp-config.php
If web process needs group-read:
chmod 440 /home/accountuser/config/wp-config.php
Immutable protection
chattr +i /home/accountuser/config/wp-config.php
Operational warning: remove immutable flag before maintenance: chattr -i /home/accountuser/config/wp-config.php.
---
5. Post-hardening validation
After applying isolation, I run this validation cycle:
php -lon final config file.- HTTP response check for site and
/wp-admin. - Media upload and plugin update test.
- Log review (
error_log,php-fpm.log,apache/nginxlogs).
Quick probe:
curl -I https://domain.tld/
curl -I https://domain.tld/wp-login.php
Expected: normal app behavior, no config disclosure.
---
6. Common field issues
Issue A: white screen after move
Common causes:
- wrong
require_oncepath; - incompatible permission model;
- typo introduced in config.
Fix:
- run
php -l; - fix ownership/permissions;
- restore backup immediately.
Issue B: cache/WAF plugin behaves unexpectedly
Cause:
bootstrap order or constant load mismatch in custom stub layout.
Fix:
- purge caches;
- verify bootstrap order;
- validate constants through controlled test script.
---
7. Rollback plan
Keep rollback ready before any change:
mv /home/accountuser/wp-config.php /home/accountuser/public_html/wp-config.php
# or in stub scenario:
cp /home/accountuser/public_html/wp-config.php.bak.YYYY-MM-DD-HHMM /home/accountuser/public_html/wp-config.php
Then:
- validate syntax;
- reload web/PHP services if needed;
- retest home/admin.
---
🏁 Strategic conclusion
Moving wp-config.php outside web root adds a critical barrier against credential exposure and limits blast radius in parser misconfiguration scenarios.
It is simple, but high-impact when combined with:
- strict permissions (
400/440); - immutable flag (
chattr +i); - operational validation and rollback discipline.
This is the difference between a functional WordPress setup and a resilient production-grade infrastructure.
This post is licensed under CC BY-NC.
Comments
Join the discussion below.
Comments are not configured yet. Add Cusdis settings in /assets/json/config/blog-comments-config.json.