wp-config.php Constants Reference: Defaults, Examples and Risks
Every wp-config.php constant that matters in an emergency or a tune-up: debugging, URLs, memory, cron, security, updates, database and multisite, each with its default, an example and the risk.
wp-config.php is the file WordPress reads before anything else. It holds the database login, the security keys and dozens of optional constants that change how WordPress behaves. Many emergency fixes are one line here: turning on error logging, forcing the right site URL, raising memory or stopping a broken cron. This reference lists each constant with its default, an example, when to use it and what can go wrong. Defaults were checked against the WordPress core source (version 7.1); older versions may differ where noted.
Placeholders: DOMAIN is your site’s host name (for example example.com). SITEUSER is the system account that owns the site files. EMAIL is an address you read. Values shown in examples such as 256M are illustrations, not recommendations for every site.
Where constants go
| Rule | Why |
|---|---|
Add custom constants above the line /* That's all, stop editing! Happy publishing. */. Current sample files also mark the start of the spot with /* Add any custom values between this line and the "stop editing" line. */ |
Everything below that line loads wp-settings.php, which defines defaults for any constant not yet set. A constant defined after that point is ignored (and PHP 8 warns that it is already defined). |
| Define each constant once. | A second define() of the same name is ignored with a warning. Search the file before adding a line. |
| Booleans and numbers are not quoted. | define( 'WP_DEBUG', true ); not 'true'. The string 'false' is truthy in PHP. |
| The file may live one folder above the WordPress root. | WordPress checks there if it is not in the root. Edit the copy that is actually loaded (wp config path tells you). |
| Keep permissions tight. | Typically 640 or 600 where the server allows it, owned by SITEUSER. Never world-writable. |
| Edit with WP-CLI when you can. | wp config set WP_DEBUG true --raw places the line correctly and avoids syntax errors. A typo in this file takes the whole site down. |
Debugging
| Constant | Default | Example | When to use | Risk |
|---|---|---|---|---|
WP_DEBUG |
false (current versions default to true when WP_ENVIRONMENT_TYPE is development or WP_DEVELOPMENT_MODE is set) |
define( 'WP_DEBUG', true ); |
Turns on PHP notices, warnings and deprecation messages. The master switch for the two below. | On its own it also displays errors, because WP_DEBUG_DISPLAY defaults to true. Pair with the next two on live sites. |
WP_DEBUG_LOG |
false |
define( 'WP_DEBUG_LOG', true ); or define( 'WP_DEBUG_LOG', '/home/SITEUSER/logs/wp-errors.log' ); |
Writes errors to wp-content/debug.log, or to a custom path (WordPress 5.1 and later). Only works when WP_DEBUG is true. |
The default wp-content/debug.log can be downloaded by anyone who guesses the URL and may reveal paths and data. Use a path outside the web root and turn logging off when done. |
WP_DEBUG_DISPLAY |
true |
define( 'WP_DEBUG_DISPLAY', false ); |
Hides errors from visitors while still logging them. Add @ini_set( 'display_errors', 0 ); for belt and braces. |
Leaving it true on a live site shows file paths to visitors and can break AJAX and REST responses. |
SCRIPT_DEBUG |
false (true only on development builds of core) |
define( 'SCRIPT_DEBUG', true ); |
Loads the non-minified versions of core JavaScript and CSS, for tracing script errors in the block editor or admin. | Larger files, slower admin. Turn off afterward. |
SAVEQUERIES |
Not defined (off) | define( 'SAVEQUERIES', true ); |
Stores every database query, its time and the calling function in $wpdb->queries. Used by Query Monitor-style analysis. |
Uses memory and slows every request. Never leave on in production. |
WP_ENVIRONMENT_TYPE |
production |
define( 'WP_ENVIRONMENT_TYPE', 'staging' ); |
Tells WordPress, plugins and themes what kind of site this is. Allowed values: local, development, staging, production. Can also be set as a server environment variable. |
A copied staging config on a live site can switch on debug behavior or disable things plugins only want off in staging. |
WP_DEVELOPMENT_MODE |
'' (off) |
define( 'WP_DEVELOPMENT_MODE', 'theme' ); |
WordPress 6.3 and later. Values core, plugin, theme or all. Disables some caching that gets in the way of active development. |
Slower on a live site. Leave empty in production. |
WP_DISABLE_FATAL_ERROR_HANDLER |
Not defined (handler on) | define( 'WP_DISABLE_FATAL_ERROR_HANDLER', true ); |
Turns off the “There has been a critical error on this website” handler and recovery mode, so raw PHP errors show (if display is on). Useful on development copies. | Removes recovery mode emails and the safe admin link on live sites. |
RECOVERY_MODE_EMAIL |
The site’s admin email setting | define( 'RECOVERY_MODE_EMAIL', 'EMAIL' ); |
Sends fatal error recovery links to a different address, such as the developer’s. | If this inbox is unread, nobody sees the recovery link. |
URLs and paths
| Constant | Default | Example | When to use | Risk |
|---|---|---|---|---|
WP_HOME |
Not defined; the home option is used |
define( 'WP_HOME', 'https://DOMAIN' ); |
Forces the public site address. The emergency fix when a wrong URL in the database causes redirects to an old domain or HTTP. | Overrides Settings, General, which then shows the field grayed out. No trailing slash. |
WP_SITEURL |
Not defined; the siteurl option is used |
define( 'WP_SITEURL', 'https://DOMAIN' ); |
Forces the address where WordPress core files live. Differs from WP_HOME only when WordPress is in a subfolder (https://DOMAIN/wp). |
A wrong value breaks the admin and login. No trailing slash. |
WP_CONTENT_DIR |
ABSPATH . 'wp-content' |
define( 'WP_CONTENT_DIR', __DIR__ . '/content' ); |
Moves the content folder. Full server path, no trailing slash. | Plugins that hard-code wp-content break. Must be set together with the URL below. |
WP_CONTENT_URL |
Site URL plus /wp-content |
define( 'WP_CONTENT_URL', 'https://DOMAIN/content' ); |
Public URL of the moved content folder. Also used to serve content from a different host. | An http:// value causes mixed content warnings on an HTTPS site. |
WP_PLUGIN_DIR / WP_PLUGIN_URL |
WP_CONTENT_DIR . '/plugins' / WP_CONTENT_URL . '/plugins' |
define( 'WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins' ); |
Moves the plugins folder only. | Same as above. |
UPLOADS |
Not defined (wp-content/uploads) |
define( 'UPLOADS', 'media' ); |
Path to uploads, relative to the WordPress root, no leading slash. | Existing attachment URLs in posts do not move with it. |
COOKIE_DOMAIN |
'' (current host) |
define( 'COOKIE_DOMAIN', 'DOMAIN' ); |
Sets the domain for login cookies. Occasionally needed for multisite domain setups. | A wrong value causes login loops: you log in and land back on the login page. |
Performance and housekeeping
| Constant | Default | Example | When to use | Risk |
|---|---|---|---|---|
WP_MEMORY_LIMIT |
40M single site, 64M multisite (or the PHP limit if WordPress cannot change it) |
define( 'WP_MEMORY_LIMIT', '256M' ); |
Memory for front-end requests. Raise it for “Allowed memory size exhausted” errors on the front end. | WordPress only ever raises PHP’s memory_limit; it cannot exceed a hard limit set by the host. A high value can hide a leaking plugin. |
WP_MAX_MEMORY_LIMIT |
256M (or higher if PHP’s limit is already higher) |
define( 'WP_MAX_MEMORY_LIMIT', '512M' ); |
Memory for admin screens and heavy tasks such as image processing. Raise for memory errors in wp-admin or during uploads. | Same host ceiling applies. |
WP_POST_REVISIONS |
true (unlimited) |
define( 'WP_POST_REVISIONS', 10 ); |
Limits stored revisions per post. false or 0 turns revisions off. |
Does not delete existing revisions. Turning them off removes the undo history editors rely on. |
AUTOSAVE_INTERVAL |
60 seconds |
define( 'AUTOSAVE_INTERVAL', 120 ); |
How often the editor autosaves. Raise it to reduce admin-ajax or REST load on busy editorial sites. | More work lost if a browser crashes. |
EMPTY_TRASH_DAYS |
30 |
define( 'EMPTY_TRASH_DAYS', 7 ); |
Days before trashed posts, pages and comments are permanently deleted. | 0 disables the trash entirely: delete becomes permanent with no undo. |
MEDIA_TRASH |
false |
define( 'MEDIA_TRASH', true ); |
Adds a trash for media items instead of immediate deletion. | Trashed files still use disk space until emptied. |
WP_CACHE |
false |
define( 'WP_CACHE', true ); |
Loads the wp-content/advanced-cache.php drop-in used by page cache plugins. Usually written by the plugin. |
Left on after removing a cache plugin, a stale advanced-cache.php can serve old pages or fatal. Remove both together. |
DISABLE_WP_CRON |
Not defined (WP-Cron runs on page loads) | define( 'DISABLE_WP_CRON', true ); |
Stops WP-Cron from spawning on visits. Use with a real system cron job running wp cron event run --due-now every few minutes. |
Without a replacement cron job, scheduled posts, backups, emails and updates silently stop. |
ALTERNATE_WP_CRON |
Not defined (off) | define( 'ALTERNATE_WP_CRON', true ); |
Runs cron by redirecting the visitor’s request instead of a loopback request. Last resort when loopback requests are blocked and system cron is not available. | Adds ?doing_wp_cron to URLs visitors see and can interfere with caching and forms. Prefer system cron. |
WP_CRON_LOCK_TIMEOUT |
60 seconds |
define( 'WP_CRON_LOCK_TIMEOUT', 120 ); |
Minimum time between cron runs, preventing overlapping runs. | Too low lets long jobs overlap; too high delays events. |
CONCATENATE_SCRIPTS |
true in the admin |
define( 'CONCATENATE_SCRIPTS', false ); |
Stops WordPress joining admin scripts into one load-scripts.php request. A classic fix when admin buttons, menus or the editor stop working behind some proxies or security rules. |
More HTTP requests in the admin. Mostly a diagnostic. |
IMAGE_EDIT_OVERWRITE |
Not defined (off) | define( 'IMAGE_EDIT_OVERWRITE', true ); |
When editing images in the media editor, overwrites the previously edited files instead of keeping each set of edits. The original is still kept for “Restore”. | Earlier edited versions are lost. |
Security
| Constant | Default | Example | When to use | Risk |
|---|---|---|---|---|
DISALLOW_FILE_EDIT |
Not defined (editor available) | define( 'DISALLOW_FILE_EDIT', true ); |
Removes the plugin and theme file editors from the admin. Recommended on every live site, because a stolen admin login can otherwise inject PHP in seconds. | Almost none. Edit code over SFTP or Git instead. |
DISALLOW_FILE_MODS |
Not defined (allowed) | define( 'DISALLOW_FILE_MODS', true ); |
Blocks installing, updating and deleting plugins and themes from the admin, the file editor, and automatic updates. For sites deployed from version control. | Security updates stop arriving unless your deployment process applies them. Never set it and walk away. |
FORCE_SSL_ADMIN |
true when the siteurl option starts with https (current versions), otherwise false |
define( 'FORCE_SSL_ADMIN', true ); |
Forces logins and the admin over HTTPS. | Behind a proxy that terminates TLS without telling WordPress, this causes a redirect loop. See the reverse proxy recipe. |
Keys and salts: AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY, AUTH_SALT, SECURE_AUTH_SALT, LOGGED_IN_SALT, NONCE_SALT |
Unique random strings set at install | wp config shuffle-salts |
Sign login cookies and nonces. Replace all eight after a compromise or when someone with access leaves. WordPress.org also serves fresh values at https://api.wordpress.org/secret-key/1.1/salt/. |
Changing them logs every user out, which is the point. Never publish them or reuse them between sites. |
WP_HTTP_BLOCK_EXTERNAL |
Not defined (outbound allowed) | define( 'WP_HTTP_BLOCK_EXTERNAL', true ); |
Blocks outbound requests made through the WordPress HTTP API, except to the site itself and localhost. Hardening for locked-down sites, or to stop a plugin calling home. | Breaks updates, license checks, payment gateways and embeds unless allowed below. |
WP_ACCESSIBLE_HOSTS |
Not defined | define( 'WP_ACCESSIBLE_HOSTS', 'api.wordpress.org,downloads.wordpress.org,*.stripe.com' ); |
Comma-separated allow list used with WP_HTTP_BLOCK_EXTERNAL. Wildcards such as *.example.com are supported. |
Only covers code that uses the WordPress HTTP API; direct cURL calls bypass it. |
DISALLOW_UNFILTERED_HTML |
Not defined | define( 'DISALLOW_UNFILTERED_HTML', true ); |
Removes the unfiltered_html capability from everyone, including administrators, so script tags and other unsafe HTML are stripped from content. |
Breaks legitimate embeds and tracking snippets pasted into content. |
ALLOW_UNFILTERED_UPLOADS |
Not defined | define( 'ALLOW_UNFILTERED_UPLOADS', true ); |
Lets administrators upload any file type. Listed so you recognize it. | Dangerous: allows uploading executable or script files. Remove after a one-off use. |
Updates and filesystem access
| Constant | Default | Example | When to use | Risk |
|---|---|---|---|---|
AUTOMATIC_UPDATER_DISABLED |
Not defined (auto-updates allowed) | define( 'AUTOMATIC_UPDATER_DISABLED', true ); |
Turns off all background updates: core, plugins, themes and translations. For sites where updates are applied by a managed process. | Security releases are no longer applied automatically. |
WP_AUTO_UPDATE_CORE |
Not defined: minor and security releases on; major releases follow the setting on Dashboard, Updates | define( 'WP_AUTO_UPDATE_CORE', 'minor' ); |
Values: true (all core updates), 'minor' (minor and security only), false (none). Core also accepts 'beta', 'rc', 'development' and 'branch-development', which behave like true for update decisions. |
false leaves known vulnerabilities unpatched. Overrides the admin setting. |
FS_METHOD |
Auto-detected | define( 'FS_METHOD', 'direct' ); |
How WordPress writes files during installs and updates: direct, ssh2, ftpext or ftpsockets. Setting direct stops the “Connection Information” FTP prompt when PHP runs as the file owner. |
Forcing direct when PHP runs as a different user than the file owner creates files owned by the web server, or fails. Fix ownership instead where possible. |
FTP_HOST, FTP_USER, FTP_PASS, FTP_SSL |
Not defined | define( 'FTP_HOST', 'ftp.DOMAIN:21' ); |
Stored credentials for the ftpext and ftpsockets methods. FTP_SSL true uses FTPS. |
Credentials in plain text in a PHP file. Prefer direct or SSH keys. |
FTP_PUBKEY, FTP_PRIKEY |
Not defined | define( 'FTP_PRIKEY', '/home/SITEUSER/.ssh/id_rsa' ); |
Key paths for the ssh2 method (needs the PHP ssh2 extension). |
The key must be unreadable by other accounts. |
FTP_BASE, FTP_CONTENT_DIR, FTP_PLUGIN_DIR |
Auto-detected | define( 'FTP_BASE', '/public_html/' ); |
Paths as the FTP account sees them, when auto-detection fails. | Wrong paths make updates fail with “could not find directory”. |
FS_CHMOD_DIR / FS_CHMOD_FILE |
Based on existing permissions, normally 0755 and 0644 |
define( 'FS_CHMOD_FILE', 0644 ); |
Permissions applied to files and folders WordPress creates. Octal, no quotes. | 0777 or 0666 lets other accounts on the server modify your site. |
Database
| Constant | Default | Example | When to use | Risk |
|---|---|---|---|---|
DB_NAME, DB_USER, DB_PASSWORD |
Set at install | define( 'DB_NAME', 'DATABASE_NAME' ); |
Database login. The first check for “Error establishing a database connection”. | Watch for stray spaces and unescaped single quotes in passwords. |
DB_HOST |
localhost in the sample file |
'localhost', '127.0.0.1', 'db.example.net:3307', 'localhost:/var/run/mysqld/mysqld.sock', '[::1]:3306' |
Host name or IP, optionally with :PORT, a Unix socket path after :, or an IPv6 address in brackets. |
localhost uses a socket while 127.0.0.1 uses TCP; database permissions can differ between the two, so one may work when the other fails. |
DB_CHARSET |
utf8mb4 in the sample file |
define( 'DB_CHARSET', 'utf8mb4' ); |
Character set for the connection. utf8mb4 supports emoji and all of Unicode. |
Changing it on an existing site does not convert stored data and can garble text. |
DB_COLLATE |
'' |
define( 'DB_COLLATE', '' ); |
Collation. Empty lets the database pick the default for the charset, which is right for almost everyone. | A collation not supported by the server (for example after moving to an older MySQL) causes errors on import. |
$table_prefix |
'wp_' |
$table_prefix = 'wp_a1b2_'; |
A variable, not a constant. Lets several installs share one database. Letters, numbers and underscores only. | Changing it on a live site needs every table renamed plus prefixed rows in the options and usermeta tables (PREFIX_user_roles, PREFIX_capabilities, PREFIX_user_level), or nobody can log in. |
WP_ALLOW_REPAIR |
Not defined | define( 'WP_ALLOW_REPAIR', true ); |
Enables the repair page at https://DOMAIN/wp-admin/maint/repair.php for crashed or corrupted tables. |
The page works without logging in. Remove the line as soon as you finish. |
CUSTOM_USER_TABLE / CUSTOM_USER_META_TABLE |
Not defined | define( 'CUSTOM_USER_TABLE', 'shared_users' ); |
Points WordPress at a different users table, to share logins between installs in one database. Set before install. | Roles are still stored per site; users may exist with no capabilities. Complex to undo. |
Multisite
| Constant | Default | Example | When to use | Risk |
|---|---|---|---|---|
WP_ALLOW_MULTISITE |
Not defined | define( 'WP_ALLOW_MULTISITE', true ); |
Unlocks Tools, Network Setup, the first step in creating a network. WordPress then gives you the lines below to add. | Harmless on its own. Converting a site to a network is hard to reverse. |
MULTISITE |
Not defined | define( 'MULTISITE', true ); |
Switches the install into network mode. | Removing it on an existing network breaks every subsite. |
SUBDOMAIN_INSTALL |
Set during network setup | define( 'SUBDOMAIN_INSTALL', false ); |
true for site.DOMAIN, false for DOMAIN/site/. |
Cannot simply be flipped later; URLs throughout the database depend on it. Subdomain networks need wildcard DNS and a certificate that covers the subdomains. |
DOMAIN_CURRENT_SITE |
Set during network setup | define( 'DOMAIN_CURRENT_SITE', 'DOMAIN' ); |
The network’s main domain. | When moving a network, the domain also has to change in the site, blogs and sitemeta tables, or you get redirect loops and “Error establishing a database connection”. |
PATH_CURRENT_SITE |
Set during network setup | define( 'PATH_CURRENT_SITE', '/' ); |
Path of the main site, usually /. |
Must match the path stored in the database. |
SITE_ID_CURRENT_SITE / BLOG_ID_CURRENT_SITE |
1 |
define( 'BLOG_ID_CURRENT_SITE', 1 ); |
IDs of the network and its main site. | Rarely changed. |
NOBLOGREDIRECT |
Not defined | define( 'NOBLOGREDIRECT', 'https://DOMAIN/' ); |
Where visitors go when they request a subsite that does not exist. | On subdirectory networks it can also redirect genuine 404s, which confuses search engines. |
WP_DEFAULT_THEME |
twentytwentyfive in 7.1 (changes with each default theme) |
define( 'WP_DEFAULT_THEME', 'THEME_SLUG' ); |
Theme used for new sites and as a fallback. | A slug that is not installed leaves new sites with a broken theme. |
Common recipes
Safe error logging on a live site
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/home/SITEUSER/logs/wp-errors.log' );
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );
Reproduce the problem, read the log, then set WP_DEBUG back to false. Create the log folder first and make sure SITEUSER can write to it.
HTTPS behind a reverse proxy, load balancer or CDN
When TLS ends at a proxy, PHP sees a plain HTTP request, so WordPress keeps redirecting to HTTPS and you get a redirect loop or mixed content. If the proxy sends X-Forwarded-Proto, tell WordPress the original request was secure. Place this above the “stop editing” line:
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) !== false ) {
$_SERVER['HTTPS'] = 'on';
}
define( 'FORCE_SSL_ADMIN', true );
Only use this when your own proxy always sets or overwrites the header; otherwise a visitor could send it themselves. With Cloudflare, prefer SSL mode Full (strict) with a valid origin certificate over Flexible, which causes this exact loop.
Locked out by a wrong site URL
define( 'WP_HOME', 'https://DOMAIN' );
define( 'WP_SITEURL', 'https://DOMAIN' );
Log in, fix Settings, General (or run wp option update home and wp option update siteurl), then remove the two lines so the setting is editable again. Replace old URLs in content with wp search-replace.
Replace WP-Cron with a real cron job
define( 'DISABLE_WP_CRON', true );
*/5 * * * * cd /PATH/TO/SITE && wp cron event run --due-now --quiet
Install the second line in SITEUSER‘s crontab (crontab -e as that user). /PATH/TO/SITE is the WordPress root.
Hardened production baseline
define( 'WP_ENVIRONMENT_TYPE', 'production' );
define( 'DISALLOW_FILE_EDIT', true );
define( 'WP_DEBUG', false );
define( 'WP_POST_REVISIONS', 20 );
define( 'FORCE_SSL_ADMIN', true );
Repair crashed tables
define( 'WP_ALLOW_REPAIR', true );
Visit https://DOMAIN/wp-admin/maint/repair.php, run “Repair Database”, then delete the line immediately. With shell access, wp db repair does the same without exposing a public page.
Memory errors
define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );
If the error still quotes the old limit, the host enforces a lower PHP memory_limit that WordPress cannot raise; change it in the hosting panel or PHP settings.
Gotchas
- A white screen after editing. One missing semicolon or quote in
wp-config.phpis a fatal error for every page. Keep a copy before editing and check withphp -l wp-config.php. - Curly quotes. Copying snippets from word processors or some web pages turns
'into typographic quotes, which PHP rejects. Use a plain text editor. - Below the line does nothing. Constants added after
require_once ABSPATH . 'wp-settings.php';arrive too late. WP_DEBUGalone shows errors to the public. BecauseWP_DEBUG_DISPLAYdefaults to true. Always set display to false on live sites.- Constants beat database settings.
WP_HOME,WP_SITEURLandWP_AUTO_UPDATE_COREoverride the admin screens, which can make settings look stuck. Check the file before chasing plugins. - Hosts add their own lines. Managed hosts often define constants in
wp-config.phpor in a file it includes. Search forrequireandincludelines before assuming a constant is unset. DISALLOW_FILE_MODSalso kills auto-updates. It is easy to forget it is there months later when a plugin vulnerability is announced.WP_ALLOW_REPAIRleft on. The repair page needs no login. Remove the line the same day.- Staging configs copied to live. Check
WP_ENVIRONMENT_TYPE,WP_HOME, debug settings and database credentials after every migration. - Secrets in backups and repositories. This file contains the database password and salts. Keep it out of public repositories and shared screenshots.
Did this fix it?
Still stuck? Start again from what you can see in the Emergency Room.
Need the full fix?
Cheat sheets are quick reference. The field guides walk you through each emergency step by step.
Open the Emergency Room