Security · · 42 min read

How to Secure wp-config.php: 15 Hardening Techniques for WordPress in 2026

Every WordPress install has one file that matters more than all the others put together. It’s not glamorous, it’s usually about sixty lines long, and most people never open it after installation. But wp-config.php is where your database password lives in plain text, and that alone makes it the file an attacker most wants to read.

I’ve spent a lot of time cleaning up sites where the actual break-in traced back to this one file being readable when it shouldn’t have been — usually not through some clever exploit, but through a forgotten wp-config.php.bak sitting in the web root, or a .git folder that shipped to production, or debug output spilling the full path and a stack trace to anyone who triggered an error.

So let me set expectations up front, because this isn’t a “move the file and you’re done” article. Securing wp-config.php properly means thinking about three separate layers:

  1. The WordPress configuration layer — the constants inside the file itself.
  2. The filesystem and web-server layer — permissions, ownership, and blocking HTTP access.
  3. The infrastructure and secrets layer — where credentials actually live, and how you’d contain the damage if one layer failed.

No single technique on this list is sufficient by itself. That’s the whole point. Defense-in-depth means that when one control fails — and eventually one will — the others are still standing between an attacker and your database.

What you’ll learn

  • Exactly what’s in wp-config.php and why each piece is sensitive
  • The realistic ways this file leaks (spoiler: rarely a direct hack)
  • 15 hardening techniques across all three layers, with Apache and Nginx examples where they make sense
  • A reference for every security-relevant constant — and which “security constants” aren’t really security controls
  • How to verify your hardening actually works, instead of assuming it does
  • What to do in the first hour if you think the file was exposed

Let’s start with what the file actually is, because understanding when WordPress loads it explains most of the risk.


What is wp-config.php?

wp-config.php is WordPress’s configuration file. It’s the bridge between the WordPress code and your database, and it’s loaded extremely early in the request lifecycle — before almost anything else WordPress does.

Here’s the flow on a normal page load:

Browser
Web Server (Apache/Nginx)
PHP (interprets the .php files)
WordPress (wp-load.php)
wp-config.phpcredentials + constants read here
Database (connects using DB_* values)

When a request comes in, the web server hands .php files to PHP, PHP starts WordPress, and one of the very first things WordPress does is require wp-config.php to learn how to connect to the database and how it should behave. The constants defined here — database credentials, security keys, debug flags, hardening switches — shape everything that follows.

That “PHP interprets it” step is the security assumption everything rests on. When PHP processes wp-config.php, it executes the code and outputs only whatever the code chooses to output — which, for a config file, is nothing. A browser requesting the file directly should get a blank page, because there’s no echo in it. The source code, including your database password, stays on the server.

The danger is any situation where that assumption breaks — where the file’s contents get served as plain text instead of being executed as code. A misconfigured server that stops handing .php to PHP, a backup copy named .txt that the server serves literally, a file-read vulnerability in a plugin — in each case, PHP never runs, and the raw text (password and all) goes straight to whoever asked.

One useful detail: WordPress will load a wp-config.php located one directory above the installation if it doesn’t find one in the install root (and there’s no other wp-config.php in the install directory). This is a deliberate, supported behavior, and it’s the basis of the first hardening technique — moving the file out of the public web root entirely.

The typical location is the WordPress root — the same directory as wp-load.php and wp-settings.php. But “typical” isn’t “required,” and that flexibility is something we’ll use.


Why wp-config.php is a high-value target

Let’s be precise about the threat, because the internet is full of “hackers instantly take over your entire site” hyperbole that doesn’t help anyone reason about real risk.

The honest version: reading wp-config.php gives an attacker your database credentials and your secret keys. What they can do with that depends entirely on what else is true about your setup — especially whether your database is reachable from where they are. It’s serious, but the consequences are conditional, not automatic.

Here are the realistic paths by which the file (or its contents) leaks:

  1. A vulnerable plugin exposes files — an arbitrary-file-read or download flaw.
  2. The web server is misconfigured.php served as text instead of executed.
  3. The PHP handler fails — a broken deploy where PHP stops processing and files serve raw.
  4. Backup files are publicly accessiblewp-config.php.bak, .old, a .zip in the web root.
  5. A Git repository leaks it — a .git directory deployed to production, or a public repo with credentials committed.
  6. Hosting panel compromise — stolen cPanel/Plesk login exposes the filesystem.
  7. Local privilege escalation — a neighbor on shared hosting reaching your files.
  8. A server-side file-read vulnerability — LFI or path traversal in application code.
  9. A malicious plugin/theme reads it — nulled software that exfiltrates config.
  10. Stolen SFTP/hosting credentials — direct filesystem access.

Notice how few of these are “someone hacked wp-config.php directly.” Most are adjacent failures — a backup, a repo, a server misconfig. That’s exactly why hardening has to be multi-layered.

Now, what could an attacker do after obtaining database credentials?

  • Direct database accessif the database is reachable from their location (often it’s bound to localhost only, which is a huge mitigation).
  • WordPress user manipulation — creating or elevating an admin account directly in wp_users/wp_usermeta.
  • Content modification — injecting spam or malicious scripts into posts.
  • Persistent backdoors — planting payloads that survive a superficial cleanup.
  • Credential theft — harvesting customer or user data.
  • Malware persistence — combined with file access, establishing long-term footholds.
  • Lateral movement — reusing the password elsewhere if you (please don’t) reused it.

Security Note: There’s a critical distinction people blur constantly. Reading wp-config.php gives an attacker secrets. Having administrative access to the server lets them do whatever they want. These are not the same thing. Someone who reads your config through a file-disclosure bug still has to reach your database to use those credentials — and if your database only accepts connections from localhost, a remote attacker with your password may be stuck at the door. Understanding which one you’re actually facing shapes your entire response.

That localhost point matters so much that it becomes a technique of its own below. Let’s get into them.


15 wp-config.php hardening techniques

Fifteen techniques, across all three layers. Work through them in order for a fresh site; cherry-pick for an existing one. None is a silver bullet — they’re layers.


Technique 1: Move wp-config.php above the web root (where supported)

Why it matters. If the file physically isn’t inside the publicly served directory, no HTTP request can ever reach it — no matter how badly the web server is misconfigured, no matter what file-disclosure bug a plugin has within the web root. You remove an entire category of exposure by removing the file from the attack surface.

How to implement it. WordPress automatically looks one directory above the install root for wp-config.php. So if WordPress lives in /var/www/example.com/public_html/, you move the file to /var/www/example.com/wp-config.php — one level up, outside public_html. WordPress finds it there with no code change required.

/var/www/example.com/
├── wp-config.phpmoved here, outside the web root
└── public_html/this is what the web server serves
    ├── index.php
    ├── wp-load.php
    └── wp-settings.php

How to verify it. Load the site normally — it should work unchanged. Then confirm no wp-config.php remains in public_html/. Requesting https://example.com/wp-config.php should return a 404 (there’s genuinely nothing there now).

Common mistake. Leaving a copy behind in the web root “just in case,” which defeats the entire purpose. Move it; don’t duplicate it. Also, this doesn’t work cleanly on every host — some managed and shared environments have their own structure or symlinks that make it awkward. Don’t force it if your host doesn’t support it.

Security impact. Eliminates HTTP-based exposure of the file entirely, and sidesteps web-server misconfiguration and many in-webroot file-disclosure bugs.

Pro Tip: This is powerful but not magic. It protects against HTTP access. It does nothing against stolen SFTP credentials, a compromised hosting panel, or a backup you dumped somewhere silly. It’s one layer.


Technique 2: Deny direct HTTP access to wp-config.php

Why it matters. If you can’t move the file above the web root (common on shared hosting), the next best thing is to tell the web server to refuse any HTTP request for it. This protects you specifically in the scenario where PHP stops executing — a broken deploy, a handler failure — and files would otherwise be served as raw text.

How to implement it — Apache. In .htaccess (or better, the vhost config), block the file by name:

# Block direct access to wp-config.php
<Files "wp-config.php">
    Require all denied
</Files>

On older Apache (2.2) the syntax was Order allow,deny / Deny from all; on modern Apache (2.4+) use Require all denied as above.

How to implement it — Nginx. Add a location block:

# Deny all access to wp-config.php
location = /wp-config.php {
    deny all;
}

How to verify it. From another machine:

curl -I https://example.com/wp-config.php

You want a 403 Forbidden (or 404), not a 200 — and definitely not the file’s contents.

Common mistake. Assuming this alone secures the file. It blocks HTTP requests. It does absolutely nothing about filesystem-level access, backups, or Git. It’s genuinely useful, but it’s a single layer that people massively overweight.

Security impact. Protects against the “PHP handler failed, file served as text” scenario and casual direct requests.

Security Note: Web-server denial protects the URL path. It does not protect the file on disk. A backup copy at a different path, a plugin reading the file through PHP, or SFTP access all bypass this rule completely. Keep reading.


Technique 3: Use restrictive filesystem permissions

Why it matters. File permissions decide which system users can read the file. Even if HTTP access is blocked, an overly permissive file can be read by other users on the same server — a real concern on shared hosting, or after a lower-privileged process is compromised.

How to implement it. Inspect current permissions first:

ls -l wp-config.php
# -rw-r--r-- 1 deployuser www-data 2314 Jul 20 09:14 wp-config.php

That output tells you the permissions (-rw-r--r-- = 644), the owner (deployuser), and the group (www-data). The right target value depends on your ownership model — which is exactly why there’s no single “correct” number.

Common mistake. Blindly running chmod 600 wp-config.php because a tutorial said so, without understanding ownership. If PHP runs as a different user than the file owner, 600 (owner-only) can make the file unreadable to PHP and take your site offline with a database connection error. The permission value and the ownership model have to match.

Security impact. Limits which local system users can read the credentials, mitigating shared-hosting snooping and post-compromise lateral reading.

There’s enough nuance here that permissions get a full section of their own later — including the 644 vs 640 vs 600 breakdown. For now: inspect, understand your ownership, then tighten.


Technique 4: Protect the database credentials themselves

Why it matters. Everything so far protects the file. But the credentials inside it are only as dangerous as the access they grant. Two sites can both leak wp-config.php; the one whose database is bound to localhost with a scoped user is in far less trouble than the one whose database accepts connections from anywhere with a root account.

How to implement it.

  • Bind the database to localhost where the app and database share a server, so DB_HOST is localhost (or 127.0.0.1) and the database simply doesn’t accept remote connections. A leaked password an attacker can’t connect with is dramatically less useful.
  • Use a strong, unique database password — long, random, generated by a password manager, and never reused anywhere else.
  • Where the database is on a separate host, restrict which IPs may connect (firewall rules, private networking) so it’s not exposed to the internet.

How to verify it. From an external machine, confirm the database port (typically 3306 for MySQL/MariaDB) is not reachable. A quick check with nc -zv your-db-host 3306 from outside your network should fail or time out for a properly firewalled database.

Common mistake. Leaving the database listening on all interfaces (0.0.0.0) when it only ever needs localhost. This turns a config leak into an immediate remote compromise.

Security impact. Turns “attacker has the password” into “attacker has a password they may not be able to use” — a massive reduction in real-world impact.


Technique 5: Use a dedicated, least-privilege database user

Why it matters. WordPress does not need a database root account. It needs to read and write its own tables in its own database. Giving it more than that means a leaked credential (or a SQL injection) can do far more damage — dropping databases, reading other applications’ data, or creating new database users.

How to implement it. Create a database user scoped to only the WordPress database, with only the privileges WordPress actually uses:

-- Create a dedicated user with a strong password
CREATE USER 'wp_example'@'localhost' IDENTIFIED BY 'use-a-long-random-password-here';

-- Grant only what WordPress needs, only on its own database
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP, CREATE TEMPORARY TABLES
  ON wp_example_db.* TO 'wp_example'@'localhost';

FLUSH PRIVILEGES;

WordPress needs the data-manipulation privileges (SELECT/INSERT/UPDATE/DELETE) for normal operation, and the schema privileges (CREATE/ALTER/INDEX/DROP) for updates and plugins that create tables. It does not need server-wide GRANT, SUPER, or FILE privileges. The @'localhost' scoping means this user can’t even connect remotely.

How to verify it. Log in as that user and confirm it can only see its own database:

SHOW GRANTS FOR 'wp_example'@'localhost';

The output should list only the WordPress database, not *.*.

Common mistake. Using the MySQL root user in DB_USER “because it was easy during setup.” It’s the single most damaging credential you could possibly put in a file designed to sometimes leak.

Security impact. Contains the blast radius. Even with the password, an attacker is confined to the WordPress database and can’t pivot across your database server.


Technique 6: Replace the default WordPress keys and salts

Why it matters. WordPress uses eight secret strings — AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY and their four matching _SALT values — to sign and secure authentication cookies and nonces. If these are left as the placeholder put your unique phrase here, or set to weak/duplicated values, the cryptographic protection around your login sessions is undermined.

How to implement it. Generate a fresh set from the official WordPress generator and paste them in, replacing the placeholders:

https://api.wordpress.org/secret-key/1.1/salt/

Every request to that URL returns a unique, randomly generated set of all eight define() lines. Or, if you have WP-CLI:

wp config shuffle-salts

How to verify it. Open wp-config.php and confirm none of the eight values still say put your unique phrase here and none are obviously weak or identical. They should look like long random strings.

Common mistake. Two of them. First, leaving the placeholder text — “put your unique phrase here” is not a secret, it’s public knowledge. Second, pasting the example block from documentation, which is also public. Always generate your own unique set.

Security impact. Ensures session cookies and nonces are protected by strong, unique secrets rather than known or weak values.

Security Note: A common misconception: these salts do not encrypt your database password, and they don’t encrypt your database. They’re used to secure authentication cookies and nonces. Rotating them logs everyone out; it does nothing to your stored data. Knowing what they don’t do keeps you from making bad decisions during an incident.


Technique 7: Rotate salts after a suspected compromise

Why it matters. Because the salts secure session cookies, changing them invalidates every existing login session — instantly logging out everyone, including an attacker riding a stolen cookie. That makes salt rotation a precise, powerful tool during incident response: it kicks out anyone currently authenticated.

How to implement it. Same as generating fresh values — paste a new set from the official generator, or run:

wp config shuffle-salts

Everyone gets logged out and must sign in again with their credentials.

How to verify it. After rotating, confirm you (and any test user) are forced to log in again. That forced re-login is the confirmation the old sessions are dead.

Common mistake. Rotating salts on a rigid schedule “for security” while thinking it’s doing more than it is. Salt rotation is not a password change and not a credential rotation — it only invalidates sessions. Rotate them when it’s useful: after a suspected compromise, a possible cookie leak, or a staff/agency offboarding — not as ritual.

Security impact. Terminates all active sessions, ejecting an attacker who has a valid session cookie and forcing re-authentication.

Here’s the distinction worth being crystal-clear on, because these three get conflated during panicked cleanups:

ActionWhat it doesWhen to use it
Password resetChanges a user’s login passwordA specific account is compromised
Salt rotationInvalidates all sessions (logs everyone out)Stolen session cookies suspected; forced logout after an incident
Credential rotationChanges infrastructure secrets (DB password, API keys)wp-config.php leaked; database credentials exposed

After a wp-config.php exposure you often need all three — because the attacker may have your DB credentials (rotate those), may have planted a session (rotate salts), and may have compromised specific accounts (reset those passwords).


Technique 8: Disable dashboard file editing with DISALLOW_FILE_EDIT

Why it matters. By default, administrators can edit theme and plugin PHP directly from Appearance → Theme File Editor and Plugins → Plugin File Editor. If an attacker gains admin access — through a stolen password, a session, or a privilege-escalation bug — that built-in editor is a ready-made way to write malicious PHP straight into your site, no file upload needed. Disabling it removes that convenience for both you and them.

How to implement it. Add this to wp-config.php, above the “stop editing” line:

define( 'DISALLOW_FILE_EDIT', true );

How to verify it. Log into wp-admin. The Theme File Editor and Plugin File Editor menu items should be gone.

Common mistake. Placing the define() below the /* That's all, stop editing! */ comment. Constants added after that line — after wp-settings.php has already loaded — often won’t take effect. Everything you add goes above that comment.

Security impact. Removes a common post-compromise code-injection path, so a compromised admin account can’t trivially write PHP through the dashboard.

Security Note: This is one of the highest value-to-effort constants there is. It doesn’t prevent the initial compromise, but it meaningfully slows down what an attacker can do afterward — and it costs you almost nothing, since editing live files from the dashboard was always a bad habit anyway.


Technique 9: Consider DISALLOW_FILE_MODS carefully

Why it matters. DISALLOW_FILE_MODS is the bigger hammer. It disables all file modifications from the dashboard — not just the editors, but plugin/theme installation, updates, and deletion. For a locked-down, deploy-only environment (code shipped via Git/CI, nothing changed on the server), that’s a strong hardening posture: an attacker with admin access can’t install a malicious plugin.

How to implement it.

define( 'DISALLOW_FILE_MODS', true );

(This also implies DISALLOW_FILE_EDIT — the editors are covered too.)

How to verify it. In wp-admin, the ability to add/update/delete plugins and themes should be gone, and the update screens should indicate updates are disabled.

Common mistake — and this is a big one: enabling it without a deployment process to handle updates another way. Because it disables automatic updates too, a site with DISALLOW_FILE_MODS set and no external update workflow will silently stop receiving security patches. That can leave you less secure overall — locked cabinet, but nobody’s replacing the expired locks. Only use this if you have a controlled, external way to apply updates (WP-CLI, CI/CD, managed host).

Security impact. Prevents dashboard-based installation of malicious plugins/themes — but trades away automatic updates, so it’s appropriate only for controlled-deployment setups.

Here’s the trade-off at a glance:

ConstantBlocks file editorBlocks install/update/deleteBlocks auto-updatesGood for
DISALLOW_FILE_EDITAlmost every site
DISALLOW_FILE_MODSDeploy-only sites with an external update process

For most sites, DISALLOW_FILE_EDIT is the right choice and DISALLOW_FILE_MODS is too aggressive. Choose deliberately.


Technique 10: Configure WP_DEBUG safely for production

Why it matters. Debug output is enormously useful in development and dangerous in production. When PHP errors display on the page, they can leak absolute file paths, plugin and theme names and versions, database query fragments, and other internal details — a free reconnaissance report for an attacker probing your site.

How to implement it. For production, debugging should be off, or logged privately rather than displayed:

// Production: no debugging output to visitors
define( 'WP_DEBUG', false );

If you need to capture errors on production temporarily (see the next technique for doing it safely):

define( 'WP_DEBUG', true );        // enable debugging
define( 'WP_DEBUG_DISPLAY', false ); // but never show errors to visitors
define( 'WP_DEBUG_LOG', true );      // write them to a log instead
@ini_set( 'display_errors', 0 );     // belt-and-suspenders: suppress display

How to verify it. Trigger a harmless PHP notice (or check pages that were throwing warnings) and confirm nothing error-related renders in the browser or page source for a normal visitor.

Common mistake. Shipping a site with WP_DEBUG and especially WP_DEBUG_DISPLAY left true from development. This is one of the most common information-disclosure issues on live WordPress sites, and it’s entirely self-inflicted.

Security impact. Prevents leakage of paths, component names/versions, and query details that help an attacker fingerprint and target your stack.


Technique 11: Prevent debug logs from becoming publicly accessible

Why it matters. Turning on WP_DEBUG_LOG is the safe way to capture errors — but by default it writes to wp-content/debug.log, which sits inside the public web root. If that file is web-accessible, you’ve moved your sensitive debug output from the screen into a file anyone can download. That’s not a fix; it’s a relocation of the problem.

How to implement it. Point the log somewhere outside the web root:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
// Log to a path OUTSIDE the public web root
define( 'WP_DEBUG_LOG', '/var/www/example.com/logs/wp-debug.log' );

If you can’t relocate it, block access to it at the web server. Apache:

<Files "debug.log">
    Require all denied
</Files>

Nginx:

location = /wp-content/debug.log {
    deny all;
}

How to verify it.

curl -I https://example.com/wp-content/debug.log

Expect a 403/404, not a 200 — and never the log contents.

Common mistake. Enabling WP_DEBUG_LOG on production, forgetting about it, and leaving debug.log sitting downloadable in wp-content/ for months, quietly accumulating paths, queries, and occasionally secrets.

Security impact. Keeps captured error detail out of attackers’ hands while still letting you diagnose issues.


Technique 12: Don’t store unnecessary secrets in wp-config.php

Why it matters. wp-config.php is a natural place to define() constants, so it tends to become a junk drawer for API keys, SMTP passwords, payment credentials, and third-party tokens. The problem: every secret you add increases the value of the file to an attacker and increases the blast radius if it ever leaks. A leaked config that only holds DB credentials is bad; one that also holds your payment gateway keys and mail credentials is a catastrophe.

How to implement it. Keep wp-config.php limited to what WordPress genuinely needs — database config, salts, and hardening constants. Push other secrets to environment variables or a secrets manager (next technique), and pull them in rather than hardcoding:

// Instead of hardcoding, read from the environment
define( 'MY_PAYMENT_API_KEY', getenv( 'MY_PAYMENT_API_KEY' ) ?: '' );

How to verify it. Read through your wp-config.php and, for every secret, ask: does WordPress need this here, or is it just convenient? Move the “just convenient” ones out.

Common mistake. Treating wp-config.php as the default home for every credential in the stack, concentrating all your secrets into one file that’s designed to occasionally leak.

Security impact. Reduces the value and blast radius of a config exposure by keeping the number of secrets in one file to a minimum.


Technique 13: Use environment variables or a secrets manager where appropriate

Why it matters. On modern infrastructure, keeping secrets out of tracked files entirely — in environment variables or a managed secret store — solves several problems at once: secrets don’t get committed to Git, they’re easier to rotate, and they can be scoped per-environment (dev/staging/prod) without editing code.

How to implement it. wp-config.php becomes a thin reader of the environment:

define( 'DB_NAME',     getenv( 'DB_NAME' ) );
define( 'DB_USER',     getenv( 'DB_USER' ) );
define( 'DB_PASSWORD', getenv( 'DB_PASSWORD' ) );
define( 'DB_HOST',     getenv( 'DB_HOST' ) ?: 'localhost' );

The actual values come from the environment — set by your host’s control panel, a .env file loaded outside the web root, Docker/Kubernetes secrets, or a managed secret store (Vault, cloud secret managers). The file you commit contains no secrets at all.

How to verify it. Confirm the site runs, then confirm the committed wp-config.php contains no literal credentials, and that any .env file is outside the web root and in .gitignore.

Common mistake. Believing environment variables are automatically secure. They’re not a magic wand — a .env file inside the web root is just as exposable as wp-config.php itself, and env vars can leak through debug output (phpinfo(), error dumps) too. The security comes from how you handle them, not from the mechanism.

Security impact. Removes secrets from tracked files, eases rotation, and separates configuration from code — when implemented carefully.

Which approach fits depends heavily on where you’re hosted:

ApproachBest forMain consideration
Traditional wp-config.phpShared hosting, simple single-server setupsMust be hardened at file + server level
Environment variablesVPS, Docker, CI/CD workflows.env must stay out of web root & Git; can leak via debug
Managed secret storeKubernetes, larger/regulated infrastructureMore moving parts; overkill for a single small site

There’s no universally “best” one — the right choice matches your architecture and your team’s ability to operate it.


Technique 14: Protect backups and stray config copies

Why it matters. This is the one that gets people. Attackers frequently don’t bother with your active wp-config.php at all — they go looking for the forgotten copies: wp-config.php.bak, wp-config.php.old, wp-config.php~, a backup.zip in the web root, a database.sql dump. These are often served as plain text (because the server doesn’t run .bak or .txt through PHP), handing over every secret with zero exploitation required.

How to implement it.

  • Never leave backup copies in the web root. Store backups outside public_html, or off-server entirely.
  • Block risky extensions at the web server as a safety net. Apache:
# Deny access to common stray-config and backup extensions
<FilesMatch "\.(bak|old|save|swp|swo|orig|sql|zip|tar|gz|txt~)$">
    Require all denied
</FilesMatch>

# Explicitly protect wp-config variants
<FilesMatch "^wp-config\.php.*$">
    Require all denied
</FilesMatch>

Nginx:

location ~* \.(bak|old|save|swp|orig|sql|zip|tar|gz)$ {
    deny all;
}
  • Audit your web root for anything that shouldn’t be there.

How to verify it. Try requesting a few likely names and confirm none return content:

for f in wp-config.php.bak wp-config.php.old wp-config.php.save wp-config.php.txt wp-config.php~ backup.zip database.sql; do
  echo -n "$f -> "; curl -s -o /dev/null -w "%{http_code}\n" "https://example.com/$f"
done

Every line should show 403 or 404, never 200.

Common mistake. Diligently hardening the live wp-config.php while a wp-config.php.bak from last year’s edit sits right next to it, fully readable. All that work, undone by the file you forgot.

Security impact. Closes the “forgotten backup” exposure — often the easiest way in, and the one attackers check first.

Common Mistake: Editing wp-config.php directly on the server and saving a .bak copy in the same folder “to be safe.” You just created the exact file an attacker is scanning for. Back up off the web root.


Technique 15: Monitor and audit wp-config.php for unauthorized changes

Why it matters. All the hardening in the world doesn’t help if you never notice when something changes. wp-config.php should be extremely stable — it changes when you change it, and essentially never otherwise. So an unexpected modification is a high-signal alarm: either something’s wrong, or someone’s in.

How to implement it.

  • File-integrity monitoring — a security tool or host feature that alerts on changes to key files, wp-config.php chief among them.
  • Check the modification time as a quick manual audit:
ls -l wp-config.php        # note the timestamp
stat wp-config.php         # more detail: modify/change times
  • Version-control awareness — if wp-config.php is deployed via your pipeline (with secrets injected separately), your VCS is your change record.

How to verify it. Make a deliberate, known change and confirm your monitoring flags it. If it doesn’t alert on a change you made, it won’t alert on one an attacker makes.

Common mistake. Setting up monitoring and never testing that the alerts actually fire — or worse, having no monitoring at all and discovering the modification weeks later during cleanup.

Security impact. Turns a silent compromise into a detected event, dramatically shrinking the window an attacker operates in unnoticed.


Deep dive: wp-config.php security constants

A reference for the constants that come up in real projects. Note the honest column: not every constant is a security control, and pretending otherwise leads to cargo-cult configuration.

ConstantWhat it doesSecurity-related?Production recommendation
DB_NAMEDatabase nameIndirect (it’s a secret)Required; keep the file protected
DB_USERDatabase usernameYes — scope this userUse a dedicated least-privilege user
DB_PASSWORDDatabase passwordYes — the crown jewelLong, random, unique
DB_HOSTDatabase hostYes — exposure surfacePrefer localhost/127.0.0.1
AUTH_KEYSigns auth cookiesYesUnique random value
SECURE_AUTH_KEYSigns SSL auth cookiesYesUnique random value
LOGGED_IN_KEYSigns logged-in cookiesYesUnique random value
NONCE_KEYSecures noncesYesUnique random value
AUTH_SALTSalt for AUTH_KEYYesUnique random value
SECURE_AUTH_SALTSalt for secure authYesUnique random value
LOGGED_IN_SALTSalt for logged-inYesUnique random value
NONCE_SALTSalt for noncesYesUnique random value
WP_DEBUGMaster debug switchYes (info disclosure)false in production
WP_DEBUG_LOGLog errors to fileYes (if log is exposed)Log outside web root if used
WP_DEBUG_DISPLAYShow errors on pageYes (info disclosure)false in production
DISALLOW_FILE_EDITDisables dashboard editorsYestrue for almost all sites
DISALLOW_FILE_MODSDisables all file mods + updatesYes (with trade-offs)Only with external update process
WP_AUTO_UPDATE_COREControls core auto-updatesYes (patching)minor (default) or true for most

A note on WP_AUTO_UPDATE_CORE: by default WordPress auto-installs minor (security/maintenance) core releases, which is what you want — those patches often close actively exploited holes within hours. Set it to true to also auto-install major releases, minor to keep the safe default explicitly, or false only if you have a controlled deployment process that applies updates another way. For a typical single site with no staging workflow, leave auto-updates on.

// Keep minor/security core auto-updates on (this is the default, stated explicitly)
define( 'WP_AUTO_UPDATE_CORE', 'minor' );

Don’t treat “has a constant” as “is a hardening step.” DB_NAME isn’t a security feature; it’s just configuration that happens to be sensitive. The salts are genuine security controls. Knowing the difference keeps your config intentional.


A secure wp-config.php example

Here’s a production-oriented skeleton using placeholders. Don’t copy it blindly — the right values (especially permissions and DB_HOST) depend on your hosting architecture, and the trade-off constants (DISALLOW_FILE_MODS) depend on your workflow.

<?php
/**
 * Production-oriented wp-config.php (placeholders — replace all values)
 */

// ── Database ────────────────────────────────────────────────
define( 'DB_NAME',     'wp_example_db' );            // WP's own database
define( 'DB_USER',     'wp_example' );               // dedicated least-privilege user
define( 'DB_PASSWORD', 'REPLACE_WITH_LONG_RANDOM' ); // strong, unique
define( 'DB_HOST',     'localhost' );                // localhost = not remotely reachable
define( 'DB_CHARSET',  'utf8mb4' );
define( 'DB_COLLATE',  '' );

// ── Authentication keys & salts (generate your OWN unique set) ──
// From: https://api.wordpress.org/secret-key/1.1/salt/
define( 'AUTH_KEY',         'REPLACE_UNIQUE_VALUE' );
define( 'SECURE_AUTH_KEY',  'REPLACE_UNIQUE_VALUE' );
define( 'LOGGED_IN_KEY',    'REPLACE_UNIQUE_VALUE' );
define( 'NONCE_KEY',        'REPLACE_UNIQUE_VALUE' );
define( 'AUTH_SALT',        'REPLACE_UNIQUE_VALUE' );
define( 'SECURE_AUTH_SALT', 'REPLACE_UNIQUE_VALUE' );
define( 'LOGGED_IN_SALT',   'REPLACE_UNIQUE_VALUE' );
define( 'NONCE_SALT',       'REPLACE_UNIQUE_VALUE' );

// ── Debugging (safe production defaults) ────────────────────
define( 'WP_DEBUG',         false );  // no debugging in production
define( 'WP_DEBUG_DISPLAY', false );  // never show errors to visitors
define( 'WP_DEBUG_LOG',     false );  // enable + relocate outside web root only when needed

// ── Hardening constants ─────────────────────────────────────
define( 'DISALLOW_FILE_EDIT',   true );    // no dashboard code editor
define( 'WP_AUTO_UPDATE_CORE',  'minor' ); // keep security auto-updates on
// define( 'DISALLOW_FILE_MODS', true );   // only if you deploy updates externally

// ── Table prefix ────────────────────────────────────────────
$table_prefix = 'wp_';  // a custom prefix is minor obscurity, not real security

/* That's all, stop editing! Happy publishing. */

if ( ! defined( 'ABSPATH' ) ) {
    define( 'ABSPATH', __DIR__ . '/' );
}
require_once ABSPATH . 'wp-settings.php';

Line-by-line, the security-relevant parts:

  • DB_HOST = localhost — the database won’t accept remote connections, so a leaked password is far less useful to a remote attacker.
  • DB_USER references a dedicated, least-privilege user (Technique 5), not root.
  • DB_PASSWORD is long, random, and unique — never reused.
  • The eight keys/salts are your own generated set, not placeholders or the documentation example.
  • WP_DEBUG / WP_DEBUG_DISPLAY / WP_DEBUG_LOG all false — no information disclosure in production.
  • DISALLOW_FILE_EDIT = true — removes the dashboard code editor as a post-compromise tool.
  • WP_AUTO_UPDATE_CORE = 'minor' — keeps security patches flowing automatically.
  • DISALLOW_FILE_MODS is commented out — a deliberate choice; enable it only if you have an external update process, because it disables auto-updates.
  • Everything sits above /* That's all, stop editing! */ — constants below that line may not take effect.

Apache protection

Apache gives you a few ways to protect the file, and it’s worth understanding the distinction between blocking HTTP access and protecting the file on disk — they’re different defenses.

Blocking HTTP access means Apache refuses requests for the file’s URL. Protecting the file on disk means filesystem permissions decide who can read it. Apache rules do the former; they do nothing for the latter.

The core rule (in .htaccess or, better, the vhost):

# Block direct HTTP access to wp-config.php
<Files "wp-config.php">
    Require all denied
</Files>

Extend it to stray copies with FilesMatch (from Technique 14):

<FilesMatch "^wp-config\.php.*$">
    Require all denied
</FilesMatch>
<FilesMatch "\.(bak|old|save|swp|orig|sql|log)$">
    Require all denied
</FilesMatch>

Pro Tip: Prefer putting these directives in your vhost/server config over .htaccess when you have access. .htaccess is re-read on every request (slower) and can be overridden or removed if an attacker gains write access to the web root. Server config can’t be tampered with from inside the web root.

The key limitation: none of this protects the file from being read on disk — by another user on shared hosting, by a PHP process, by SFTP. Web-server denial and filesystem permissions are complementary layers, not substitutes. You need both.


Nginx protection

Nginx doesn’t use .htaccess — rules live in the server/location config and take effect on reload. The rule to deny the file:

# Deny direct access to wp-config.php
location = /wp-config.php {
    deny all;
}

And for stray copies and backups:

location ~* /wp-config\.php.* {
    deny all;
}
location ~* \.(bak|old|save|swp|orig|sql|log)$ {
    deny all;
}

A few Nginx-specific cautions:

  • Test before reloading. Always run nginx -t to validate the config before systemctl reload nginx. A syntax error on reload can take the whole site down.
  • A misplaced rule can break things. Location-block ordering and regex precedence in Nginx are unforgiving; a too-broad deny can block legitimate paths. Test on staging.
  • PHP-FPM config matters. Nginx passes .php to PHP-FPM via fastcgi_pass. If your PHP location block is misconfigured, you can end up serving .php as text (the exact disaster we’re guarding against) — so make sure your PHP handling is correct, not just your deny rules.
  • HTTP denial is one layer. Same as Apache: this blocks requests, not disk reads.

Security Note: The single most dangerous Nginx misconfiguration for this file is one where PHP isn’t processed for some path and the raw .php gets served. Denying wp-config.php by name helps, but verify your overall PHP handling is sound — a broad “serve static files” rule that accidentally catches .php is how config files leak wholesale.


File permissions

Permissions decide which system users can read wp-config.php. There is no single universally correct value — it depends on the relationship between the file owner and the user PHP runs as. Let’s make that concrete.

Inspect what you have:

ls -l wp-config.php
# -rw-r----- 1 deployuser www-data 2314 Jul 20 09:14 wp-config.php
#  ^^^^^^^^^   ^^^^^^^^^^ ^^^^^^^^
#  permissions  owner      group

The common candidates:

PermissionMeaningWorks when…Risk if misused
644 (-rw-r--r--)Owner writes; everyone readsVery common defaultAny local user can read the file
640 (-rw-r-----)Owner writes; group reads; others noneOwner = you, group = web-server userGood balance on many setups
600 (-rw-------)Owner onlyOwner is the PHP userSite breaks if PHP runs as a different user

The logic:

  • 644 is often the default but is loose — “others” (any local user) can read it. On shared hosting that can mean neighbors.
  • 640 is frequently the sweet spot: the owner (your deploy user) can write, the group (set to the web-server/PHP user, e.g. www-data) can read, and everyone else is shut out. PHP reads it via group membership; strangers can’t.
  • 600 is tightest but only works when the file’s owner is the same user PHP runs as. Get the ownership model wrong and PHP can’t read its own config — instant “Error establishing a database connection.”

So the right value is inseparable from ownership. A typical clean model: files owned by your deploy user, group set to the web-server user, wp-config.php at 640. But managed hosts often run their own model (some use per-account PHP users where 600 is correct), so check your host’s setup before changing anything.

Common Mistake: Copy-pasting chmod 600 wp-config.php from a tutorial without checking who owns the file and who PHP runs as. If they differ, you’ve just taken your site offline. Inspect ownership first, then choose the permission that matches it.


Database security (because protecting the file isn’t enough)

Hardening wp-config.php protects the credentials from being read. It does nothing about what those credentials can do. If the file ever leaks despite your best efforts, database-side controls are what limit the damage — so they’re not optional extras, they’re the second half of the same job.

  • Dedicated database user scoped to the WordPress database only (Technique 5) — not shared across apps, not root.
  • Least privilege — only the privileges WordPress uses; no server-wide GRANT, SUPER, or FILE.
  • No root accounts in DB_USER, ever. Root in a config file is the worst-case leak.
  • Strong, unique database password — long, random, used nowhere else.
  • Credential rotation after any suspected exposure (and this means actually changing the password in the database and the file, not just editing the file).
  • Restrict database host exposure — bind to localhost where possible; firewall/private-network it when it’s on a separate host.
  • Access restrictions — the database should accept connections only from where WordPress actually runs.

The throughline: a leaked credential should be as useless as you can possibly make it. Localhost binding, least privilege, and a unique password together mean that even a full config disclosure doesn’t automatically equal a database takeover.


Secret keys and salts (what they do and don’t do)

Let’s clear up the confusion around salts once and for all, because it drives bad incident-response decisions.

What they do: WordPress’s eight keys and salts are used to cryptographically sign and secure your authentication cookies and nonces. When you log in, WordPress issues a cookie signed using these secrets; the salts make those cookies tamper-resistant and hard to forge.

What happens when you rotate them: every existing cookie becomes invalid, so every logged-in user is logged out and must sign in again. Content, settings, and data are untouched. That’s it.

When rotation is appropriate: after a suspected compromise, a possible cookie/session leak, or offboarding someone with access — any time you want to forcibly end all active sessions. Rotating on a rigid calendar “for hygiene” is largely theater; rotate when there’s a reason.

And the misconception to kill:

Security Note: Salts do not encrypt your database password. They do not encrypt your database contents. They do not protect wp-config.php itself. They secure authentication cookies and nonces — nothing more. If your config leaks, rotating salts logs attackers out of active sessions but does nothing about the database credentials they may have grabbed. That’s why salt rotation and credential rotation are separate steps (see Technique 7’s table).

Keep the three actions distinct: password reset (a user’s login), salt rotation (all sessions), credential rotation (infrastructure secrets like the DB password). An exposure incident usually needs all three.


Debugging security

Debug configuration deserves its own focus because misconfigured debugging is one of the most common — and most avoidable — information-disclosure problems on live WordPress sites.

The three constants:

  • WP_DEBUG — the master switch. true enables WordPress’s debug mode; false (production) disables it.
  • WP_DEBUG_DISPLAY — controls whether errors render on the page. This is the dangerous one. true prints errors where visitors (and attackers) can see them.
  • WP_DEBUG_LOG — routes errors to a log file instead of the screen. Safe if the log isn’t publicly reachable.

Safe production configuration:

define( 'WP_DEBUG',         false );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG',     false );

And when you genuinely need to capture production errors temporarily, do it without displaying anything and log outside the web root:

define( 'WP_DEBUG',         true );
define( 'WP_DEBUG_DISPLAY', false );              // critical: nothing on screen
define( 'WP_DEBUG_LOG', '/var/www/example.com/logs/wp-debug.log' ); // outside web root
@ini_set( 'display_errors', 0 );

Why displaying errors publicly is a real problem — a single visible PHP error can leak:

  • Absolute file paths (/var/www/example.com/public_html/wp-content/plugins/...) — revealing your directory structure and hosting layout.
  • Plugin and theme names and versions — a shopping list of components to check for known vulnerabilities.
  • SQL query fragments — hints about your schema and, occasionally, data.
  • Internal application state — variable dumps, function traces, and other implementation detail.

And the debug log risk: enabling WP_DEBUG_LOG writes to wp-content/debug.log by default — inside the web root. Leave it enabled and forgotten, and that downloadable file becomes a slow leak of everything above. Relocate it or block it (Technique 11), and never leave it on in production longer than a specific debugging session.


Backups and wp-config.php copies

I’m giving this its own section even though it’s Technique 14, because in my experience it’s the single most common actual cause of config exposure — far more than exotic vulnerabilities.

The usual suspects, all of which tend to serve as plain text because the server doesn’t run them through PHP:

  • wp-config.php.bak
  • wp-config.php.old
  • wp-config.php.save (nano’s rescue file)
  • wp-config.php.txt
  • wp-config.php~ (editor backup)
  • wp-config.php.swp / .swo (Vim swap files)
  • backup.zip, site-backup.zip
  • database.sql, dump.sql
  • an exposed .git/ directory or a public repo with credentials committed

Attackers run automated scanners that request these exact names on thousands of sites. They’re not being clever; they’re checking whether you were careless. And often enough, someone was.

A practical audit checklist for your web root:

  • No wp-config.php.* variants anywhere in the web root
  • No editor artifacts (~, .swp, .save, .orig)
  • No .zip/.tar.gz/.sql archives sitting in public directories
  • No .git/ directory served (block it, or deploy without it)
  • No .env file inside the web root
  • Backups stored off the web root or off-server entirely
  • Web-server rules deny the risky extensions as a safety net (Technique 14)

Common Mistake: Making a “quick backup” by copying wp-config.php to wp-config.php.bak in the same folder before an edit. That .bak is exactly what the scanners are looking for, and it’ll happily serve as text. Back up to somewhere outside the web root.


How attackers find configuration leaks (defensive awareness)

Understanding how leaks get discovered helps you test your own site the same way — purely defensively. Attackers typically find config exposure through:

  • Exposed backup files — automated scans for the stray-copy names above.
  • Misconfigured web servers — probing for paths where .php serves as text.
  • Source repositories — checking for accessible .git/ directories or public repos with committed secrets.
  • File-disclosure vulnerabilities — exploiting LFI/path-traversal bugs in plugins.
  • Debug output — triggering errors to harvest paths and component info.
  • Deployment artifacts — leftover installer files, phpinfo.php, test scripts.

The constructive use of this list is to run the same checks against your own infrastructure (next section), so you find the leak before someone else does. This is standard defensive practice — you’re auditing what you own.


How to verify wp-config.php is secure

Hardening you haven’t verified is hardening you’re only assuming. Here’s a practical audit:

  • Direct HTTP access blockedcurl -I returns 403/404, not the file
  • File permissions reviewed — matched to your ownership model (ls -l)
  • Ownership reviewed — owner and group make sense for your setup
  • Database user is least-privilegeSHOW GRANTS shows only the WP database
  • Strong database password — long, random, unique
  • Security keys configured — all eight are unique, no placeholders
  • Debug display disabled — no errors render for visitors
  • Debug log protected — not web-accessible (or disabled)
  • File editing disabledDISALLOW_FILE_EDIT set where appropriate
  • Backups protected — stored off the web root
  • No stray wp-config copies — none reachable over HTTP
  • No credentials in Git — repo scanned, .gitignore correct
  • Secrets not needlessly duplicated — minimal secrets in the file
  • File-change monitoring active — and tested to actually alert
  • Hosting/server access secured — SFTP over keys, panel on MFA

Testing examples

Safe, non-destructive tests you can run against your own site.

Is the file blocked over HTTP?

curl -I https://example.com/wp-config.php

You want HTTP/2 403 or 404. A 200 — especially one whose body contains define( — means the file is being served, which is an emergency.

Security Note: A 403/404 status is necessary but not sufficient to call the file “secure.” The status only tells you the HTTP path is blocked. It says nothing about filesystem permissions, whether a .bak copy exists at another path, whether the DB user is over-privileged, or whether the credentials are in a Git repo. Don’t let one green check create false confidence.

Check permissions and ownership:

ls -l wp-config.php
stat wp-config.php

Check for an exposed debug log:

curl -I https://example.com/wp-content/debug.log   # want 403/404

Check for stray backups (loop from Technique 14):

for f in wp-config.php.bak wp-config.php.old wp-config.php.save wp-config.php.txt wp-config.php~ backup.zip database.sql; do
  echo -n "$f -> "; curl -s -o /dev/null -w "%{http_code}\n" "https://example.com/$f"
done

Check for an exposed Git directory:

curl -I https://example.com/.git/config   # want 403/404, NOT 200

Check database privileges (as the WordPress DB user):

SHOW GRANTS FOR CURRENT_USER();

The output should list only your WordPress database — not ALL PRIVILEGES ON *.*.


Common wp-config.php security mistakes

Fifteen I see repeatedly, each with the fix implied:

  1. Leaving WP_DEBUG_DISPLAY on in production — leaks paths, versions, queries to anyone who triggers an error.
  2. Using the database root account in DB_USER — worst-case blast radius on any leak.
  3. Scattering API secrets throughout the file — concentrates all your secrets in the one file designed to sometimes leak.
  4. Leaving a wp-config.php.bak in the web root — the easiest possible exposure, served as plain text.
  5. Loose permissions (644 on shared hosting) — lets neighbors read the file.
  6. Assuming .htaccess alone solves it — it blocks HTTP, not disk reads or backups.
  7. Broken Nginx rules that serve .php as text — the catastrophic misconfig that dumps the whole file.
  8. Forgetting debug.log — leaving it enabled and downloadable for months.
  9. Not rotating salts after a serious compromise — leaving an attacker’s session valid.
  10. Committing secrets to Git — they live in history forever, even after “deletion.”
  11. Copying config from random tutorials — including their public example salts and mismatched permissions.
  12. Applying 600 without checking ownership — takes the site offline when PHP runs as a different user.
  13. Blocking wp-config.php but ignoring backup copies — hardening the front door, leaving the window open.
  14. Never auditing file changes — a silent modification goes unnoticed for weeks.
  15. Treating constants as complete securityDISALLOW_FILE_EDIT is great, but it’s one layer, not a strategy.

Hardening by environment: shared hosting vs VPS vs Docker

The best configuration depends heavily on where you’re running. What’s ideal on a VPS may be impossible on shared hosting.

EnvironmentRecommended approachMain riskBest practice
Shared hostingWeb-server deny rules + tight permissions (host-permitting)Neighbor access; limited server controlStrong DB password, DISALLOW_FILE_EDIT, block backups; lean on host’s protections
Managed WordPressTrust host’s hardening; focus on app-level configLess control; host-dependentVerify host blocks direct access; still set salts, debug, DISALLOW_FILE_EDIT
VPSMove file above web root + 640 perms + localhost DBMisconfiguration is on youFull stack: file location, permissions, server rules, DB binding
DockerEnv vars / secrets, config outside imageSecrets baked into images; .env leaksInject secrets at runtime; never commit .env; keep it out of the image
KubernetesManaged secret store (Secrets/Vault)Complexity; misconfigured secret mountsMount secrets at runtime; scope with RBAC; audit access

The pattern: more control = more responsibility. On shared hosting you rely on the host for a lot; on a VPS or in containers, every layer is yours to get right. Match your effort to what your environment actually allows and requires.


Incident response: what if wp-config.php was exposed?

If you have reason to believe the file (or its contents) leaked — a public .bak found, a Git exposure, a file-disclosure bug — assume the credentials are compromised and work this in order. Changing the database password alone is not enough if the attacker already established persistence.

PriorityStepWhy
1Assume DB credentials are compromisedAct on worst-case; don’t wait for proof
2Rotate database credentialsChange the DB password and update wp-config.php
3Update wp-config.phpNew DB password, and while you’re in there, verify hardening
4Rotate WordPress saltsLog out all sessions, ejecting any riding a stolen cookie
5Review administrator accountsLook for rogue/elevated admins created via direct DB access
6Review recently modified filesFind injected code or planted backdoors
7Inspect plugins/themesCheck for tampering or malicious additions
8Check server access logsFind how/when the file was accessed and from where
9Check the database for suspicious changesInjected content, rogue options, spam
10Scan for malware/backdoorsPersistence often outlives the initial fix
11Review API credentialsRotate anything else that was in the file
12Check backupsEnsure you have a clean one; ensure backups aren’t the leak source
13Re-secure the serverRotate SFTP/hosting/panel credentials; the leak may be broader
14Monitor for reinfectionWatch for weeks; attackers commonly return

Security Note: The reason step 4 (salt rotation) sits alongside step 2 (credential rotation) is that they solve different problems. Rotating the DB password stops future use of the leaked credential. Rotating salts kills any session the attacker already established. Do only one and you may leave the attacker a way back in. And if you find evidence of persistence — a backdoor, a rogue admin — a credential change won’t remove it; you’re now in full malware-cleanup territory.


15-point final hardening checklist

Save or print this.

  • Move wp-config.php above the web root (where supported)
  • Deny direct HTTP access at the web server
  • Set permissions matched to your ownership model (often 640)
  • Verify correct owner/group
  • Bind the database to localhost / restrict its exposure
  • Use a dedicated least-privilege DB user (never root)
  • Use a strong, unique database password
  • Generate your own unique keys and salts (no placeholders)
  • Set DISALLOW_FILE_EDIT (and consider DISALLOW_FILE_MODS if you deploy externally)
  • Disable debug display in production
  • Keep debug logs out of the web root
  • Minimize secrets stored in the file
  • Protect backups; remove stray wp-config copies
  • Keep credentials out of Git; block .git/ exposure
  • Monitor the file for unauthorized changes (and test the alert)

Found this helpful? Share it with your network.

Written by
Abhira

Security researcher and WordPress specialist contributing in-depth analysis and hardening guides.

Leave a Comment

Your email address will not be published. Required fields are marked *