The fastest way to take down a healthy WordPress site is not a bad plugin. It’s a “tiny” change in wp-config.php made under pressure: the DB host swapped, a debug flag left on, a reverse proxy header assumed, a cache drop-in half-installed.
Because wp-config.php sits early in the boot path, mistakes here don’t degrade gracefully. They fail loudly, or worse: they limp along while quietly leaking information, skipping cron, or writing logs until your disk taps out.
What wp-config.php really does (and why mistakes hurt)
wp-config.php is not just “where the database password lives.” It’s the control panel for how WordPress boots: where it reads data from, what it assumes about the request scheme/host, how it logs, which caching layer it tries to load, and which security tokens it trusts.
Most config files are read late, after the framework initializes. WordPress reads wp-config.php early and often. That has two operational consequences:
- Failure modes are immediate. Syntax errors, bad constants, wrong include paths: you get white screens, 500s, or “Error establishing a database connection.”
- Misbehavior can be subtle. The site “works,” but cron doesn’t fire, admin cookies invalidate, mixed content shows up, object cache thrashes, or your logs fill at a rate that would impress a DDoS bot.
Principles that keep you out of trouble
- Make changes reversible. If you can’t roll it back in seconds, you’re not “configuring,” you’re gambling.
- Prefer environment-driven secrets. The fewer hardcoded credentials, the less likely they end up in a git repo, backup archive, or pastebin tragedy.
- Don’t treat wp-config.php as a dumping ground. It’s tempting. Resist. If you’re defining ten unrelated constants to band-aid plugin behavior, you’re building a shrine to future outages.
One small warning sign: when a team says “It’s just a config tweak,” that’s usually when you should schedule a maintenance window.
Joke 1: wp-config.php is like a break-glass box. If you’re opening it casually, you’re already having a day.
Fast diagnosis playbook
This is the “stop the bleeding” flow. It’s biased toward production: minimal changes, maximum signal. Do these in order unless you already have a smoking gun.
First: confirm the failure class (PHP parse vs WordPress vs infra)
- Check HTTP status and response body. 500 with empty body often means PHP fatal/parse; “Error establishing a database connection” is WordPress catching DB connect failures.
- Tail PHP-FPM / web server error logs. If you see “Parse error” referencing
wp-config.php, stop and fix syntax before anything else. - Validate PHP can read the file. Wrong permissions can look like “missing config” behavior.
Second: validate database connectivity from the app host
- Resolve DB host. DNS drift causes real outages.
- Connect with the same credentials. Prove the username/password and network path are valid.
- Confirm charset/collation expectations. Mis-set values don’t always block boot, but they can corrupt data over time or break migrations.
Third: assess request scheme/host assumptions
- Behind reverse proxy? If
$_SERVERheaders aren’t trusted correctly, you get redirect loops, mixed content, or admin login failures. - Check
WP_HOMEandWP_SITEURL. One wrong character can strand your admin behind endless redirects.
Fourth: look for “helpful” toggles that became liabilities
- Debug flags.
WP_DEBUGon in production is a performance tax and a data leak risk. - Cache drop-ins. Redis/Memcached object cache misconfigs lead to login weirdness and CPU spikes that masquerade as “WordPress is slow.”
- Cron settings. Disabling WP-Cron without a real system cron is how newsletters stop sending and nobody notices until marketing does.
If you only remember one thing: prove basics first—syntax, permissions, DB reachability—before you debate caching philosophy.
Interesting facts and history you can use
- Fact 1: WordPress historically used
wp-config-sample.phpas the template; people still copy it verbatim, including outdated assumptions about salts, debug, and DB host defaults. - Fact 2: The
$table_prefixsetting exists partly because WordPress anticipated shared databases and multiple installs in one schema—common in early budget hosting. - Fact 3: WordPress “drop-ins” (like
object-cache.php) are loaded before most plugins. A broken drop-in can break everything while your plugin list looks innocent. - Fact 4: The config supports
define('WP_CACHE', true)largely to coordinate with caching plugins, but setting it without the rest of the stack is a classic half-install. - Fact 5:
DISABLE_WP_CRONwas a pragmatic response to traffic-dependent scheduling. On low-traffic sites, WP-Cron is basically “maybe later.” - Fact 6: Many hosts run PHP as a different user (FPM pool user) than your SSH user. File permissions that “look fine” in your shell can be unreadable to PHP.
- Fact 7:
FORCE_SSL_ADMINpredates the modern “everything behind TLS” norm. It can still matter when you have split TLS termination or mixed proxy layers. - Fact 8: WordPress can take DB constants from environment variables. This is not just trendy “12-factor” talk; it’s a real way to avoid committing secrets into images or repos.
- Fact 9: The canonical location of
wp-config.phpcan be one directory above the document root. This is old-school hardening, and it still works.
Most of these features were born from shared hosting constraints and then carried into serious production. That’s why wp-config.php can feel like an archaeological dig.
Practical tasks: commands, outputs, and decisions
These are “run it now” tasks. Each includes: the command, what the output means, and what decision you make next. They’re written for a typical Linux host running Nginx/Apache + PHP-FPM + MySQL/MariaDB, but the logic holds elsewhere.
Task 1: Verify wp-config.php exists and isn’t a dangling symlink
cr0x@server:~$ ls -la /var/www/html/wp-config.php
-rw-r----- 1 www-data www-data 3890 Dec 27 10:11 /var/www/html/wp-config.php
What it means: The file exists, is regular, and readable by the web user group. If you see wp-config.php -> /some/path, confirm the target exists.
Decision: If missing or unreadable, fix path/permissions before doing anything else. WordPress can’t “work around” missing config.
Task 2: Check permissions (and catch the “SSH user vs PHP user” trap)
cr0x@server:~$ namei -l /var/www/html/wp-config.php
f: /var/www/html/wp-config.php
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwxr-xr-x root root html
-rw-r----- www-data www-data wp-config.php
What it means: Directory execute bits allow traversal; the final file is readable. If any directory on the path lacks execute for the PHP user, PHP can’t reach the file.
Decision: Fix directory traversal permissions rather than “chmod 777” anything. If you’re considering 777, walk away, get coffee, then do it properly.
Task 3: Validate wp-config.php has no PHP parse errors
cr0x@server:~$ php -l /var/www/html/wp-config.php
No syntax errors detected in /var/www/html/wp-config.php
What it means: PHP can parse it. Common failures: stray BOM, missing semicolon, copied smart quotes from a ticket comment, or an extra <?php.
Decision: If syntax errors appear, fix those first. Everything else is noise until parsing succeeds.
Task 4: Confirm DB constants are present (without printing secrets broadly)
cr0x@server:~$ sudo -u www-data php -r 'include "/var/www/html/wp-config.php"; echo DB_NAME, "\n", DB_HOST, "\n";'
wordpress_prod
db01.internal
What it means: The config loads under the same user PHP runs as, and constants resolve. If include fails, you’ll see warnings/fatals.
Decision: If this fails, focus on permissions/paths/includes. If it works, move to network/DB reachability.
Task 5: Verify DNS resolution for DB_HOST
cr0x@server:~$ getent hosts db01.internal
10.20.30.41 db01.internal
What it means: The host resolves. If it doesn’t, WordPress may try and fail repeatedly, spiking PHP workers.
Decision: If DNS fails, fix DNS/hosts file, or use an IP temporarily (with a plan to revert). Don’t “fix” WordPress when it’s really DNS.
Task 6: Check TCP connectivity to the database port
cr0x@server:~$ nc -vz db01.internal 3306
Connection to db01.internal 3306 port [tcp/mysql] succeeded!
What it means: Network path is open. If you get timeouts/refused, it’s firewall, security group, DB down, or wrong port.
Decision: If TCP fails, don’t rotate DB passwords. Fix network reachability first.
Task 7: Authenticate to MySQL using the same credentials WordPress uses
cr0x@server:~$ mysql -h db01.internal -u wp_user -p -e "SELECT 1;"
Enter password:
1
1
What it means: Credentials work and the DB server responds. If authentication fails, the error message is gold: “Access denied,” “Unknown database,” etc.
Decision: If auth fails, verify DB_USER, DB_PASSWORD, and grants. If the database name is wrong, fix DB_NAME rather than creating a fresh empty schema “to make it work.”
Task 8: Identify request scheme/host from the edge (reverse proxy sanity)
cr0x@server:~$ curl -I http://127.0.0.1 | sed -n '1,10p'
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sat, 27 Dec 2025 10:20:04 GMT
Content-Type: text/html
Content-Length: 162
Location: https://example.com/
What it means: You’re seeing a redirect. If you’re behind a proxy terminating TLS, the origin may think it’s HTTP and redirect incorrectly.
Decision: If you have redirect loops, check WP_HOME/WP_SITEURL and proxy header handling before touching plugins.
Task 9: Check for redirect loop from the public host
cr0x@server:~$ curl -sS -o /dev/null -D - https://example.com/wp-admin/ | sed -n '1,12p'
HTTP/2 302
date: Sat, 27 Dec 2025 10:21:11 GMT
content-type: text/html; charset=UTF-8
location: https://example.com/wp-login.php?redirect_to=https%3A%2F%2Fexample.com%2Fwp-admin%2F&reauth=1
set-cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; secure; HttpOnly
What it means: A normal admin flow redirects to login. If you repeatedly bounce between http/https or different hostnames, config is lying about the canonical URL.
Decision: Fix canonical URLs in config (or DB) and correct proxy scheme detection. Do not “solve” with a random redirect plugin.
Task 10: Confirm debug mode and whether logs are growing
cr0x@server:~$ grep -nE "WP_DEBUG|WP_DEBUG_LOG|WP_DEBUG_DISPLAY" /var/www/html/wp-config.php
92:define('WP_DEBUG', false);
93:define('WP_DEBUG_LOG', true);
94:define('WP_DEBUG_DISPLAY', false);
What it means: Debug is off, but logging is on. That combination is sometimes intentional, but it can still write a lot if plugins emit notices.
Decision: If disk usage is rising, either fix the noisy code or disable debug logging in production and route errors to PHP-FPM/syslog with proper rotation.
Task 11: Watch log size and rotate pressure (storage engineer’s favorite outage)
cr0x@server:~$ sudo du -sh /var/www/html/wp-content/debug.log
3.6G /var/www/html/wp-content/debug.log
What it means: That file is enormous. On a small root volume, this is how “the website is down” becomes “the server is down.”
Decision: If the log is large, stop the write amplification: disable debug logging, truncate safely, and add rotation. Also investigate why it grew (often a single repeated notice in a hot path).
Task 12: Check filesystem free space (because config mistakes spill onto disks)
cr0x@server:~$ df -h /var/www
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 40G 39G 600M 99% /
What it means: You are one deploy away from downtime. At 99%, MySQL and PHP-FPM start failing in creative ways.
Decision: Free space immediately (logs, old releases, caches), then fix the underlying cause (often debug logs, backups stored on the same volume, or runaway cache directories).
Task 13: Verify object cache drop-in and whether WordPress is attempting to use it
cr0x@server:~$ ls -la /var/www/html/wp-content/object-cache.php
-rw-r--r-- 1 www-data www-data 14523 Dec 27 09:42 /var/www/html/wp-content/object-cache.php
What it means: A drop-in exists. That means WordPress will load it early. If it’s broken or points to a dead Redis host, you can get slow admin, login failures, or fatal errors.
Decision: If you suspect cache issues, temporarily move it out of the way (rename the file) during a maintenance window and retest.
Task 14: Test Redis connectivity (if you configured it in wp-config.php)
cr0x@server:~$ redis-cli -h 127.0.0.1 ping
PONG
What it means: Redis is reachable and responding. If you get a timeout, it’s either down, listening elsewhere, protected by ACL, or blocked by firewall.
Decision: If Redis is unreachable, disable the object cache integration until it’s fixed. Broken caches are worse than no cache.
Task 15: Confirm PHP memory limit and whether WordPress is overriding it
cr0x@server:~$ php -i | grep -i "^memory_limit"
memory_limit => 256M => 256M
What it means: PHP’s baseline memory limit is 256M. WordPress can request more via constants like WP_MEMORY_LIMIT, but it can’t exceed PHP’s hard cap if configured that way.
Decision: If you’re seeing “Allowed memory size exhausted,” raise PHP-FPM’s limit (and then find out what’s eating memory; don’t just inflate limits forever).
Task 16: Validate canonical WordPress URLs in the database when config isn’t defining them
cr0x@server:~$ mysql -h db01.internal -u wp_user -p -D wordpress_prod -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
Enter password:
option_name option_value
home https://example.com
siteurl https://example.com
What it means: DB values are consistent. If they disagree (or contain the wrong host/scheme), you get redirect loops, broken asset URLs, and admin pain.
Decision: If wrong, fix them carefully. Prefer a controlled update and then clear caches. Don’t “temporarily” hardcode constants and forget them for years.
Task 17: Check cron behavior if DISABLE_WP_CRON is set
cr0x@server:~$ grep -n "DISABLE_WP_CRON" /var/www/html/wp-config.php
110:define('DISABLE_WP_CRON', true);
What it means: WP-Cron is disabled. That is fine only if you have a real system cron calling wp-cron.php.
Decision: If you don’t have a system cron, re-enable WP-Cron or add a cron job immediately. Otherwise scheduled posts, cleanups, and background jobs stall silently.
Common mistakes: symptoms → root cause → fix
This is the section you’ll come back to at 02:00. It’s designed to map what you see to what’s actually wrong, then give you a concrete fix.
1) White screen / HTTP 500 right after editing wp-config.php
Symptoms: Blank page, 500 error, or “There has been a critical error” immediately after a config change.
Root cause: PHP syntax error (missing semicolon, stray quote), BOM at file start, or duplicate <?php tags.
Fix: Run php -l against the file; revert to the last known-good version. If you copied/pasted from a ticket, check for “smart quotes.”
2) “Error establishing a database connection”
Symptoms: WordPress front-end and admin show the DB error page; health checks fail.
Root cause: Wrong DB_HOST, wrong credentials, missing privileges, DNS resolution issue, or DB server down.
Fix: Prove network connectivity (nc) and authenticate with mysql using the same creds. Only rotate credentials after you confirm the DB is reachable and healthy.
3) Redirect loops (especially after adding a reverse proxy/CDN)
Symptoms: Browser says “too many redirects,” admin login bounces forever, or HTTP/HTTPS flips repeatedly.
Root cause: WordPress believes requests are HTTP when they’re HTTPS at the edge; WP_HOME/WP_SITEURL mismatched; proxy headers not trusted.
Fix: Set canonical URLs consistently and handle HTTP_X_FORWARDED_PROTO only when coming from trusted proxies. Don’t blindly trust forwarded headers from the internet.
4) Mixed content warnings and broken CSS/JS
Symptoms: Pages load but assets are blocked; admin looks unstyled; console shows mixed content errors.
Root cause: siteurl/home or WP_HOME/WP_SITEURL using http:// while the site is served via https://.
Fix: Standardize on HTTPS in both config and DB. If behind proxy, correct scheme detection so WordPress generates HTTPS URLs.
5) Debug info leaking into pages
Symptoms: Visitors see warnings/notices; page HTML includes file paths and query details.
Root cause: WP_DEBUG enabled with WP_DEBUG_DISPLAY on, or PHP display_errors enabled at the FPM level.
Fix: In production: WP_DEBUG false, WP_DEBUG_DISPLAY false. Log errors elsewhere with rotation. If you need debug, enable temporarily and time-box it.
6) Disk fills up unexpectedly
Symptoms: Server goes read-only or services crash; df shows 95–100% usage.
Root cause: WP_DEBUG_LOG generating a huge wp-content/debug.log; plugin spamming notices; or a misconfigured cache writing endlessly.
Fix: Stop log growth (disable debug log), truncate the file safely, add rotation, and fix the underlying noisy code path.
7) Login cookies invalidating or admin sessions randomly expiring
Symptoms: You log in, then immediately get logged out; “Are you sure you want to do this?” nonces fail.
Root cause: Changing salts/keys (or rotating them too aggressively), inconsistent COOKIE_DOMAIN, reverse proxy causing host changes, or caching of admin pages.
Fix: Keep salts stable unless you’re intentionally invalidating sessions. Do not cache /wp-admin or logged-in pages at edge. Ensure a single canonical host and cookie domain.
8) Object cache enabled but performance gets worse
Symptoms: Higher TTFB, more CPU, admin slow; intermittent errors in logs from cache backend.
Root cause: Redis/Memcached unreachable/slow, wrong socket/host, authentication mismatches, or cache drop-in not compatible with PHP version.
Fix: Validate backend connectivity, measure latency, and disable the drop-in until stable. Caching that fails open with timeouts is a self-inflicted DoS.
9) Multisite behaves strangely after a migration
Symptoms: Subsites redirect incorrectly; uploads missing; network admin broken.
Root cause: Multisite constants mismatch (MULTISITE, SUBDOMAIN_INSTALL, DOMAIN_CURRENT_SITE, path constants), or stale DB URLs.
Fix: Treat multisite config as a unit. Validate domain/path constants against the DB and the reverse proxy routing.
10) Security regression: exposed DB creds or writable config
Symptoms: Credentials discovered in a repo, backups, or world-readable files; malware edits config.
Root cause: Weak file permissions, secrets committed, or config in document root with misconfigured server allowing source download (rare, but catastrophic).
Fix: Lock down permissions, keep secrets out of repos, consider moving wp-config.php one directory up, and verify server never serves PHP source.
Joke 2: Leaving WP_DEBUG on in production is like leaving your car hood open to “improve cooling.” You will learn new sounds.
Three corporate mini-stories from production
Mini-story 1: The incident caused by a wrong assumption
The company was migrating WordPress from a single VM to a “proper” setup: a managed database, a load balancer, and two app nodes. The migration plan looked solid. The config change looked harmless: set DB_HOST to the managed DB endpoint and ship it.
Outage hit within minutes. Both nodes returned “Error establishing a database connection.” Someone swore the credentials were correct because they “worked on their laptop.” That phrase should be printed on a warning label and taped to every on-call phone.
The root issue was not the password. It was name resolution inside the VPC. The managed DB endpoint resolved to a private address, but the app nodes were pointed at a different resolver that didn’t know that private zone. The endpoint didn’t resolve consistently, so PHP workers piled up retrying connections. Health checks failed. The load balancer drained the nodes. Total outage, even though “the DB is up.”
The fix was boring: correct DNS configuration and test resolution from the app subnet before any config change. They added a preflight script that runs getent hosts and a TCP connect test from each node during deploy. The next migration went smoothly, because they stopped assuming “DNS will just work.”
Mini-story 2: The optimization that backfired
A performance push arrived with the usual executive background music: “We need the site faster.” Engineering responded with Redis object caching. Reasonable. They added the drop-in, set WP_CACHE, and defined a Redis host in config. Load tests improved. Everyone congratulated everyone.
Then production traffic hit. Latency got worse, not better. CPU rose on the app nodes. PHP-FPM worker counts climbed. The database load dropped, which looked good until you noticed the site was slower. One of those “we optimized the wrong thing” weeks.
The actual issue: Redis was reachable, but it lived behind a network hop that added occasional latency spikes. The object-cache drop-in was configured with a connect timeout that was generous in human terms and disastrous in request-path terms. When Redis stalled, each request waited. Worse, the cache miss penalty triggered more PHP work while workers were stuck waiting on the cache. That’s how “caching” becomes a distributed throttling mechanism.
The fix was to treat Redis as production infrastructure, not a plugin checkbox. They moved Redis closer (or onto the same network tier), tightened timeouts, and monitored cache latency like it was a database. When Redis was unhealthy, they preferred failing open quickly rather than waiting. The performance improvements returned—because the cache stopped being the slowest dependency.
Mini-story 3: The boring but correct practice that saved the day
A different team ran WordPress as a revenue-critical property. They weren’t flashy. They were meticulous. Every config change was deployed via a pipeline that performed three checks: syntax lint, permission check under the web user, and a DB connectivity probe using the configured host and user.
One afternoon, a routine credential rotation went sideways. The secrets manager updated, but one app node didn’t refresh its environment variables due to a process supervisor bug. Half the fleet had new DB credentials, half didn’t. The symptom was ugly: intermittent DB connection failures depending on which node the load balancer picked.
The pipeline caught it before it became a full outage. The DB probe failed on one node, and the deploy halted. The on-call got a clear error: “Cannot authenticate with configured credentials from node X.” They drained that node, restarted services to pick up the new environment, and reintroduced it. Customers never noticed.
The lesson was not “use environment variables.” It was: test the config the way production uses it, every time. Boring checks prevent exciting incidents.
paraphrased idea — John Allspaw: reliability comes from designing for how systems fail, not from hoping careful people won’t make mistakes.
Checklists / step-by-step plan
Step-by-step: safely editing wp-config.php in production
- Take a copy with permissions preserved.
cr0x@server:~$ sudo cp -a /var/www/html/wp-config.php /var/www/html/wp-config.php.bakMeaning:
-apreserves mode/ownership/timestamps. Your rollback won’t create a new permissions problem.Decision: If you can’t back it up, you’re not ready to edit it.
- Edit with a tool that won’t add BOM or smart quotes. Use
vim,nano, or a sane editor over SSH. - Lint before reload.
cr0x@server:~$ php -l /var/www/html/wp-config.php No syntax errors detected in /var/www/html/wp-config.phpDecision: If lint fails, revert immediately and re-edit calmly.
- Validate it under the web user.
cr0x@server:~$ sudo -u www-data php -r 'include "/var/www/html/wp-config.php"; echo "loaded\n";' loadedDecision: If this fails, you likely introduced a path/include/permission problem that your root shell hides.
- Hit a lightweight endpoint and watch logs. Prefer a single request + log tail to a full browser refresh storm.
Hardening checklist (do this, don’t argue)
- File permissions: readable by PHP, not world-readable. Typical:
640owned bywww-dataor root with group readable. - Move wp-config.php up one directory when your layout allows it, so it’s not inside the document root.
- Disable debug display in production; log somewhere controlled with rotation.
- Rotate salts intentionally (incident response) not casually. Rotating salts logs out every user and invalidates sessions.
- Do not trust forwarded headers by default. Only honor them from known proxy IPs.
- Keep
WP_HOME/WP_SITEURLconsistent. If you define them in config, document why and where the source of truth lives. - Confirm cron strategy: either WP-Cron enabled, or system cron in place. “Disabled with no replacement” is not a strategy.
Performance checklist (the version that won’t sabotage you)
- Measure before enabling object cache. If your bottleneck is PHP CPU or slow plugins, Redis won’t save you.
- Set aggressive timeouts for caches. A cache that stalls requests is worse than no cache.
- Keep debug logging off in hot paths. Disk I/O is not a free side quest.
- Align memory limits with reality. Raise limits only if you also fix the cause (plugins, huge queries, image processing spikes).
What to put in wp-config.php (and what not to)
Database settings: correct, minimal, testable
At minimum: DB_NAME, DB_USER, DB_PASSWORD, DB_HOST. Everything else should be justified. Don’t sprinkle experimental constants because a blog post said so. Blogs don’t get paged.
Salts and keys: don’t improvise crypto
The auth keys and salts protect cookies and nonces. If they change, sessions invalidate. That can be intentional during incident response. It should not be part of a casual deploy. Store them securely and keep them stable.
Table prefix: not a security silver bullet
Changing $table_prefix from wp_ is fine, but don’t oversell it. It’s weak defense against SQL injection; the real fix is to not have injection vulnerabilities. Still, unique prefixes help when hosting multiple sites in one DB and reduce accidental collisions during migrations.
Reverse proxy and HTTPS handling: be explicit and cautious
Many outages come from one line that forces HTTPS incorrectly, or trusts X-Forwarded-Proto from anyone. If you must override scheme detection in wp-config.php, do it with IP allowlists at the web server/proxy, not with wishful thinking inside PHP.
Debug settings: pick either “safe logging” or “temporary chaos,” not both
In production, the clean pattern is: do not display errors; log errors in an infrastructure-controlled location; rotate logs; alert on rate spikes. If you need verbose debugging, time-box it and have a rollback plan.
Automatic updates and file modifications: understand the blast radius
Disabling file edits and controlling updates can be responsible in regulated environments. It can also leave you running vulnerable plugins for months because “updates are scary.” Decide based on your patch pipeline maturity, not superstition.
FAQ
1) Can I store DB credentials in environment variables instead of wp-config.php?
Yes, and it’s often better operationally. You reduce the chance of committing secrets. But you must ensure the PHP-FPM service reliably receives updated environment on deploy/restart, and you need a clear rollback path.
2) Should wp-config.php be owned by root or www-data?
Either can be correct. Root-owned with group-readable by the PHP user group is a common hardening pattern. The point is: PHP must read it; attackers (and random users) should not write it.
3) Is changing $table_prefix worth it for security?
It’s minor defense-in-depth, not a shield. Do it if you’re provisioning a new install or consolidating databases. Don’t do it as a panic response to an active compromise; focus on patching, creds rotation, and forensic containment.
4) Why does my site work on the front-end but wp-admin is broken after config changes?
Admin paths are more sensitive to scheme/host/cookie settings. Reverse proxy mis-detection, cookie domain issues, or caching rules that accidentally cache logged-in responses often hit wp-admin first.
5) What’s the safest way to enable debugging in production briefly?
Turn on logging, not display. Keep it time-boxed and monitored for file growth. Prefer logging at PHP-FPM/web server layer with rotation rather than dumping into wp-content/debug.log.
6) My “Error establishing a database connection” goes away if I refresh. What gives?
Intermittent DB connections usually mean network flaps, DNS instability, DB max connections, or slow authentication. It can also be PHP-FPM overload causing timeouts. Treat it as an infrastructure problem until proven otherwise.
7) Should I define WP_HOME and WP_SITEURL in wp-config.php?
Only if you have a strong reason (immutable infrastructure, container images, or frequent environment changes). If you set them, you’ve made the DB values irrelevant—document that, or the next person will “fix” the DB and nothing changes.
8) Does wp-config.php affect performance directly?
Yes, indirectly. Debug logging, object caching config, cron toggles, and SSL handling can all change latency and load. A single misconfigured cache timeout can become the slowest dependency in every request.
9) How do I avoid taking the site down when editing wp-config.php?
Backup, lint, test under the web user, and deploy through a controlled path. And if you can, use a canary node behind the load balancer for config changes.
Next steps that prevent repeat incidents
If your WordPress outages have a greatest hits album, wp-config.php is probably track one. The fixes are not exotic. They’re disciplined.
- Turn your “Fast diagnosis” into a runbook. Syntax, permissions, DB reachability, scheme/host correctness, debug/caching toggles.
- Add preflight checks to deploy. Lint the config, include it as the PHP user, and run a DB connect probe using the configured host and user.
- Make logging safe by default. No debug display in production. Rotate logs. Alert on log growth and disk usage.
- Be conservative with caching changes. Validate cache backend latency and set timeouts that fail fast.
- Document canonical URL and proxy behavior. Most redirect loops are policy ambiguity pretending to be a bug.
Do those, and wp-config.php goes back to being what it should be: a boring file you rarely think about. Boring is good. Boring scales.