{"id":2009895,"date":"2022-05-06T20:00:00","date_gmt":"2022-05-06T20:00:00","guid":{"rendered":"https:\/\/wpx.net\/blog\/?p=2009895"},"modified":"2023-11-01T11:09:14","modified_gmt":"2023-11-01T11:09:14","slug":"wpconfig-immense-power","status":"publish","type":"post","link":"https:\/\/wpx.net\/blog\/wpconfig-immense-power\/","title":{"rendered":"The Immense Power of wp-config.php"},"content":{"rendered":"\n<p>The thing that motivates me in writing these columns for WPX.net is my desire to help you achieve mastery over WordPress. Mastery implies having knowledge and exerting control.<\/p>\n\n\n\n<p>There is one file within each WordPress installation that allows an inordinate amount of control over the behavior of the entire instance \u2014 core, themes, and even some plugins: the <strong>wp-config.php<\/strong> file. You can use it to unlock hidden functionality and impose limits on users&#8217; behavior and performance that are impossible to override from the WordPress dashboard.<\/p>\n\n\n\n<p>I have already mentioned wp-config.php in passing in a couple of previous columns. In this article, it will be the centerpiece.<\/p>\n\n\n\n<p>Interestingly, the <a rel=\"noopener noreferrer\" href=\"https:\/\/codex.wordpress.org\/Main_Page\" target=\"_blank\">WordPress Codex<\/a> does not have a separate article about wp-config.php configuration parameters. There is no explanation of how to use all of them.<\/p>\n\n\n\n<p>I am aiming to improve that. When published, this column will be the most detailed collection of documented wp-config settings in existence, as far as I know. I also intend to come back to it regularly and update it when I come across some new and helpful settings.<\/p>\n\n\n\n<p>This isn&#8217;t the kind of blog post that one is expected to read from beginning to end (but please feel free to skim through it at least the first time you see it). It has been structured like a reference guide. If you find any of this information useful, bookmark it and check back from time to time.<\/p>\n\n\n\n<p>Let us dig into what wp-config.php can offer us.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Database_Access_and_Configuration\"><\/span>Database Access and Configuration<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Almost everyone knows that wp-config.php holds database credentials. If you ever see the &#8216;Error establishing a database connection&#8217; message when opening your WordPress website, the very first thing you need to do is open up-config.php and check out the settings for <code>DB_NAME<\/code>, <code>DB_USER<\/code>, <code>DB_PASSWORD<\/code> and <code>DB_HOST<\/code>.<\/p>\n\n\n\n<p>Near these are two other less well-understood parameters: <code>DB_CHARSET<\/code> and <code>DB_COLLATE<\/code>. The Database Character Set determines the encoding used within the database. Since WordPress 4.2, the default and recommended setting for this is &#8216;<strong>utf8mb4<\/strong>&#8216; which allows the database to store the full, 32-bit range of Unicode symbols. This should be the default value for any new WordPress instance you create.<\/p>\n\n\n\n<p>The collation setting determines how symbols are sorted within the selected character set. The recommended value for this setting is &#8216;<strong>utf8mb4_unicode_ci<\/strong>&#8216; but if your WordPress website is designated to use with a specific language, you might select that specific collation. The rule of thumb here is that if you need to change the collation, you <strong>know<\/strong> you have to; otherwise, you should stick with Unicode, case-insensitive.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Keys_and_Salts\"><\/span>Keys and Salts<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Below the database settings is a block of 4 keys and 4 salts. WordPress uses the four keys to encrypt certain parts of its data and salts the data stored in the browser cookies with the other 4 strings to prevent cookie forgery. These values should <strong>never<\/strong> be shared with anybody.<\/p>\n\n\n\n<p>Regenerating the keys and salts is the fastest way to stop site hijacking via stolen browser cookies. Replacing them has the effect of immediately invalidating all existing sessions (including yours, as the administrator).<\/p>\n\n\n\n<p>Bear in mind that somebody who is finalizing a purchase from your Woocommerce store as you are resetting the keys and salts will lose their cart contents, but there will be no long-lasting effects or consequences besides that.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Force_Use_of_HTTPS\"><\/span>Force Use of HTTPS<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>These days, it is less frequent to see websites that do not use SSL connections to serve pages to their visitors. Usually, this is handled at the server level; and if your website uses HSTS headers, even at the domain level.<\/p>\n\n\n\n<p>However, if a particular site does not use SSL by default, you might want to make sure that at least the admin area is using an encrypted connection. To do that, you may add this setting to wp-config.php<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define('FORCE_SSL_ADMIN', true);<\/code><\/pre>\n\n\n\n<p>Note: older WordPress tutorials mention another setting, FORCE_SSL_LOGIN, which enforces an encrypted connection for login pages. This one is deprecated since WordPress 4.4; even if you put it in wp-config.php, it will be ignored.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Post_and_Media_Management_and_Housekeeping\"><\/span>Post and Media Management and Housekeeping<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>WordPress has a very robust autosave and versioning for blog posts, which allows users to effortlessly restore their content to an earlier state.<\/p>\n\n\n\n<p>By default, WordPress will enable auto-save and store a copy of your post in the database and in the browser&#8217;s cache once every 60 seconds. Depending on your preferences, you might want to change that. You do this by adding the following line to wp-config.php:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'AUTOSAVE_INTERVAL', 120 ); \/\/ Defaults to 60 seconds<\/code><\/pre>\n\n\n\n<p>It is not currently possible to completely disable autosave, but providing a large enough value will do the same. Hint: there are 86,400 seconds in a 24-hour day.<\/p>\n\n\n\n<p>Keeping previous posts revisions can be a lifesaver, but it also fills the database with many copies of your posts. This could quickly turn into an issue if you have many writers working in parallel. That is why WordPress allows you to limit the number of post revisions it will store, or to disable revisioning altogether:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_POST_REVISIONS', 3 );\ndefine( 'WP_POST_REVISIONS', false ); \/\/ Disable feature<\/code><\/pre>\n\n\n\n<p>Note: changing this setting will not influence the number of post revisions for existing posts, unless you edit each one of them afterwards. Plugins like <a rel=\"noopener noreferrer\" href=\"https:\/\/wordpress.org\/plugins\/wp-optimize\/\" target=\"_blank\">WP-Optimize<\/a> can help you clean up revisions of old publications.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Media_Management\"><\/span>Media Management<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Unlike blog posts, pages, comments, and other kinds of data, WordPress treats images very roughly. It allows users to delete them and offers no means of restoring them once this is done.<\/p>\n\n\n\n<p>The solution to this apparent injustice is the following pair of wp-config.php settings:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define('MEDIA_TRASH', true) ;  \/\/ Trash media files instead of delete\ndefine('EMPTY_TRASH_DAYS', 7); \/\/ Empty trash after 7 days<\/code><\/pre>\n\n\n\n<p>The first one enables the trash bin for media images, and the second one determines how long they are kept there before getting deleted for real. Bear in mind that enabling EMPTY_TRASH_DAYS is not obligatory: if you leave it out of the config file, media images will simply stay in the trash until manually deleted.<\/p>\n\n\n\n<p>By default, the media library will only accept files of certain formats. While this list is long, you might find yourself in a situation when you need to upload a file and publish it on your WordPress website, except WordPress would have none of it. The easiest way to allow this is to enable the following settings:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define('ALLOW_UNFILTERED_UPLOADS', true);<\/code><\/pre>\n\n\n\n<p>However, bear in mind that this has the potential to significantly degrade the WordPress security model. It allows privileged users to upload unchecked files that might be infected with malware, or \u2014 if you are uploading shell scripts and executable files \u2014 hackers could make attempts to call them from the outside. This option should be used with extreme caution, and\/or only occasionally (for example, you could enable it while you upload a specific file, link to it from a blog post, and then disable unfiltered uploads once more).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Memory_Allocation\"><\/span>Memory Allocation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>WordPress allows users to set or increase the limit to the usage of system RAM. If you ever get a PHP error that says &#8216;<code>Allowed memory size of ... bytes exhausted (tried to allocate ... bytes)<\/code>&#8216;, then it is time to increase your RAM limit. The way to do that is to add the following setting to wp-config.php<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define('WP_MEMORY_LIMIT', 128M);<\/code><\/pre>\n\n\n\n<p>Having more RAM at our disposal could be helpful for heavier websites with complicated functionality. You should always remember however that usually there is a memory limit setting imposed globally on your hosting account, and that you can&#8217;t rely on WP_MEMORY_LIMIT to get access to indefinitely more memory than is allotted to you by the webhost.<\/p>\n\n\n\n<p>However, it is important to know that WP_MEMORY_LIMIT has a less-known sister function that allows administrators to control RAM usage in the back end independently of the front end.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define('WP_MAX_MEMORY_LIMIT', 256M);<\/code><\/pre>\n\n\n\n<p>WP_MAX_MEMORY_LIMIT overrides the memory limit for PHP processes executing in the back end, which could help those who use WordPress as a CRM service or something similar. But once again, you must remember that you are limited by the memory allocation that your host provides.<\/p>\n\n\n\n<p>Side note: this limit is very often 128MB, and more rarely 256MB. However WPX \u2014 being the awesome host they are \u2014 allow their clients to set up a max memory limit of 1024MB.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Catch_and_Process_404_Not_Found_Requests\"><\/span>Catch and Process 404 Not Found Requests<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>I honestly think that allowing users to see a 404 &#8216;Not Found&#8217; page is a small tragedy. As a webmaster, you should do your utmost to prevent such situation. Thankfully, WordPress makes things much easier for you: it can be configured to intercept all 404 requests and send them to a custom page, and you only need to create that page.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* Catch 404 pages and redirect to a custom page *\/\ndefine('NOBLOGREDIRECT', 'https:\/\/YOURDOMAIN.COM\/are-you-lost');<\/code><\/pre>\n\n\n\n<p>I have planned a blog post specifically dedicated to creating custom 404 pages. However that one is still many weeks away from completion, and you might not have time to wait for me. In such a case, open Google, and search for &#8220;awesome 404 error pages&#8221; to get some ideas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"EnableDisable_Theme_and_Plugin_Editor\"><\/span>Enable\/Disable Theme and Plugin Editor<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>WordPress ships with a quite convenient file editor that allows users to make changes to theme and plugin components. On production websites, these functions should of course stay disabled and hidden.<\/p>\n\n\n\n<p>But if you are working on a theme or a plugin and need to study the code, this is the fastest way to view it and make changes if needed.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'DISALLOW_FILE_EDIT',  false );\ndefine( 'DISALLOW_THEME_EDIT', false );<\/code><\/pre>\n\n\n\n<p>Pasting these two inside wp-config.php will add two new entries to your WordPress admin bar:<\/p>\n\n\n\n<p>Plugins \u27a1&nbsp;Plugin File Editor<br>Appearance \u27a1&nbsp;Theme File Editor<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Change_Site_URL_and_Home_Directory\"><\/span>Change Site URL and Home Directory<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>These next two settings are extraordinarily powerful.<\/p>\n\n\n\n<p>When you provision a new WordPress instance, the installer sets up two very important internal parameters: WP_HOME and WP_SITEURL. These were initially meant to be used when somebody wanted to configure WordPress to appear as if it opens from the default domain URL but without cluttering the www root folder with extra files.<\/p>\n\n\n\n<p>Hence, WordPress allows you to provide separate settings for the physical locations of your WordPress folder and the publicly-facing domain. You would set WP_HOME to <a href=\"https:\/\/example.com\" target=\"_blank\" rel=\"noopener\">https:\/\/example.com<\/a> and WP_SITEURL to <a href=\"https:\/\/example.com\/wordpress\/\" target=\"_blank\" rel=\"noopener\">https:\/\/example.com\/wordpress\/<\/a>, for example. Combined with some .htaccess trickery, when visitors attempt to load example.com, WordPress would serve them from example.com\/wordpress but the URLs in the address bar would still appear as if they originate from example.com.<\/p>\n\n\n\n<p>This was important maybe 10 or 15 years ago when WordPress had little adoption and had to offer anything it could to make it easy for webmasters to deploy it parallel to some other CMS or static website.<\/p>\n\n\n\n<p>Nowadays almost nobody would think about tucking WordPress away in a subfolder (&#8216;Nobody puts Baby in the corner!&#8217;), but the WP_HOME and WP_SITEURL settings are still supported, and can be used for something way more powerful, like changing the entire apparent URL of the domain, or making the same WordPress instance serve content under multiple domains!<\/p>\n\n\n\n<p>The values for WP_HOME and WP_SITEURL can be changed from the admin dashboard (Settings \u27a1 General, WordPress Address and Site Address), and are stored in the wp-options table. Normally, this limits WordPress to responding only to requests that match the domain and URL stored in these settings.<\/p>\n\n\n\n<p>However, we can use wp-config.php to override the default WordPress behavior and add the following lines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_SITEURL', 'http:\/\/example.com\/wordpress' );\ndefine( 'WP_HOME',    'http:\/\/example.com' );<\/code><\/pre>\n\n\n\n<p>WP_SITEURL and WP_HOME will override the matching values stored within the wp_options table, but will not change them permanently. This makes these settings extremely helpful when you need to promptly migrate the WordPress instance to a different domain, or to make a quick copy of it for testing\/staging purposes and let yourself in to make permanent changes.<\/p>\n\n\n\n<p>We can even use PHP environment variables to make the address replacement dynamic. Consider this setting:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define ( 'WP_SITEURL', 'https:\/\/' . $_SERVER&#91;'HTTP_HOST'] );\ndefine ( 'WP_HOME', 'https:\/\/' . $_SERVER&#91;'HTTP_HOST'] );<\/code><\/pre>\n\n\n\n<p>Now imagine that your web server is configured to serve 5 different domains: example.com, example.net, example.gov, example.edu, example.org. As long as these domains are configured to resolve to the IP address of your web server, and you have configured the www_root for each vhost to point towards the physical folder where your WP instance is located, WordPress will respond to each inquiry as if it has been configured to serve each domain natively!<\/p>\n\n\n\n<p>One final word about site relocation: there might be another way to do it using a similar setting called RELOCATE that I learned about recently, but haven&#8217;t used in practice.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define ( 'RELOCATE', true );<\/code><\/pre>\n\n\n\n<p>It is mentioned in the WordPress codex, under <a rel=\"noopener noreferrer\" href=\"https:\/\/wordpress.org\/support\/article\/changing-the-site-url\/\" target=\"_blank\">the main article that discusses changing a WordPress websites&#8217; URL<\/a>. As far as I can tell, the difference between the previous method and this one is that RELOCATE reads the values of HTTP_HOST and PATH_INFO from the server environment and changes the content of WordPress Address URL and Site Address URL in Settings \u27a1 General.<\/p>\n\n\n\n<p>In a sense, the behavior is similar to that of using WP_SITEURL and WP_HOME with the HTTP_HOST server variable, which makes RELOCATE a convenient shortcut.<\/p>\n\n\n\n<p>It is very important to disable this setting as soon as you&#8217;re done moving the website.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Auto-Update_Control\"><\/span>Auto-Update Control<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>WordPress has four types of components that require periodic updates:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>WordPress core<\/li>\n\n\n\n<li>3rd-party plugins<\/li>\n\n\n\n<li>Themes<\/li>\n\n\n\n<li>Translations<\/li>\n<\/ul>\n\n\n\n<p>Under current versions, it is possible to enable auto-updates for all of these component types. However, every update carries a minimal risk of breaking something. That is why WordPress gives us the ability to control its update patterns and even prohibit updates completely (not that I recommend this).<\/p>\n\n\n\n<p>The good thing about the settings I am about to show you is that they override any settings within the control panel. As long as you prevent your customers from touching wp-config.php, you can be sure you will be in full control of updates.<\/p>\n\n\n\n<p>Here are the possibilities.<\/p>\n\n\n\n<p>1. Disable All Automatic Updates<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* Disable all automated updates                      *\/\n\/* Including core, plugins, themes and translations   *\/\ndefine( 'AUTOMATIC_UPDATER_DISABLED', true );<\/code><\/pre>\n\n\n\n<p>2. Control Core Automatic Updates<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* Enable ALL core updates, including minor and major: *\/\ndefine( 'WP_AUTO_UPDATE_CORE', true );<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* Disable ALL core updates, including minor and major: *\/\ndefine( 'WP_AUTO_UPDATE_CORE', false);<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* Enable minor updates ONLY *\/\ndefine( 'WP_AUTO_UPDATE_CORE', 'minor' );<\/code><\/pre>\n\n\n\n<p>3. Disable Automatic Updates for bundled themes (e.g. Twenty-Twenty) and plugins (Hello Dolly, Akismet)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* Disable updates for bundled themes and plugins *\/\ndefine( 'CORE_UPGRADE_SKIP_NEW_BUNDLED', true );<\/code><\/pre>\n\n\n\n<p>4. Disable plugin and theme installations and updates altogether:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'DISALLOW_FILE_MODS', true );<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Changing_Resource_Folder_Locations\"><\/span>Changing Resource Folder Locations<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>WordPress allows you to change the default locations for different parts of the CMS like plugins, uploads, themes, etc. This can help you free space on a partition or simplify migration when mounting extra storage. You can specify relocation to a different local folder (i.e. specify a relative path) or a different host (i.e. specify a URL).<\/p>\n\n\n\n<p>Here are the possibilities:<\/p>\n\n\n\n<p><strong>1. Move everything:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_CONTENT_DIR', dirname(__FILE__) . '\/blog\/wp-content' );\n- or -\ndefine( 'WP_CONTENT_URL', 'http:\/\/example\/blog\/wp-content' );<\/code><\/pre>\n\n\n\n<p><strong>2. Move plugins only:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_PLUGIN_DIR', dirname(__FILE__) . '\/blog\/wp-content\/plugins' );\n- or -\ndefine( 'WP_PLUGIN_URL', 'http:\/\/example\/blog\/wp-content\/plugins' );<\/code><\/pre>\n\n\n\n<p><strong>3. Move languages folder only:<\/strong><\/p>\n\n\n\n<p>There are several ways to do this.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_LANG_DIR', WP_CONTENT_DIR . '\/languages' );\ndefine( 'WP_LANG_DIR', ABSPATH . WPINC . '\/languages' );\ndefine( 'LANGDIR', WPINC . '\/languages' );\ndefine( 'LANGDIR', 'wp-content\/languages' );<\/code><\/pre>\n\n\n\n<p><strong>4. Move user uploaded content only:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'UPLOADS', 'blog\/wp-content\/uploads' );<\/code><\/pre>\n\n\n\n<p>This one is very important because the uploads folder holds all user-uploaded media files which could easily grow in size. To prevent disk shortage from happening, the system administrator may choose to keep the uploads in a different mounting share.<\/p>\n\n\n\n<p><strong>5. Move themes only:<\/strong><\/p>\n\n\n\n<p>Weirdly enough, it is not possible to relocate the themes folder alone; the theme URL is hardcoded as a sub-folder to wp-content.<\/p>\n\n\n\n<p>You should be very careful with this functionality and perform thorough tests. While the WordPress core will likely have no issues, badly coded plugins and themes can cause the website to break.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Development_Environments\"><\/span>Development Environments<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>This functionality is not intended to directly influence the behavior of WordPress. Instead, it is supposed to help the theme, plugin developers put a different kind of behavior based on the the state\/intended use for a specific instance.<\/p>\n\n\n\n<p>WordPress hosts and providers of WordPress development\/staging environments could, for example, modify the behavior of their back-ends by querying the newly created <a rel=\"noopener noreferrer\" href=\"https:\/\/developer.wordpress.org\/reference\/functions\/wp_get_environment_type\/\" target=\"_blank\">wp_get_environment_type()<\/a> function and hook into it if necessary. A query can be used for something as simple as indicating the state of the instance in the GUI dashboard, or for more complicated changes to the control structure like automatically disabling caching\/CDN.<\/p>\n\n\n\n<p>When creating a new website copy through some kind of automated procedure, a web host could add the state of that new instance in wp-config.php<\/p>\n\n\n\n<p>This variable presently supports 4 states: <strong>local<\/strong>, <strong>development<\/strong>, <strong>staging<\/strong>, and <strong>production<\/strong> (production is the default one).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_ENVIRONMENT_TYPE', 'development' );\ndefine( 'WP_ENVIRONMENT_TYPE', 'staging' );\ndefine( 'WP_ENVIRONMENT_TYPE', 'local' );\ndefine( 'WP_ENVIRONMENT_TYPE', 'production' );  \/\/ Default<\/code><\/pre>\n\n\n\n<p>As I am writing this, there is no difference in the way the instance behaves except one thing: when <strong>the development<\/strong> state is selected, the instance will automatically enable WP_DEBUG as well, even if is not specifically activated.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Debugging_and_Troubleshooting_PHP_Code\"><\/span>Debugging and Troubleshooting PHP Code<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>WordPress has a debug mode that can be used to troubleshoot errors by either logging them, displaying them on screen, or both. For most shared hosts that lack shell access, logging errors to a file is impractical, because that file can&#8217;t be watched as it updates in real time. That is why it makes sense to enable on-screen bug reporting.<\/p>\n\n\n\n<p>In order to reproduce errors when visiting specific pages, we also need to turn off the automated WordPress function that prevents pages with PHP errors from loading. These are the four settings that you need to activate to enable full debug mode.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Disable Fatal Error Handle\ndefine( 'WP_DISABLE_FATAL_ERROR_HANDLER', true );\n\/\/ Enable WP_DEBUG\ndefine( 'WP_DEBUG', true );\n\/\/ Show errors on screen\ndefine( 'WP_DEBUG_DISPLAY', true );\n\/\/ Write errors to a log file located at wp-content\/debug.log\ndefine( 'WP_DEBUG_LOG', true );<\/code><\/pre>\n\n\n\n<p>Bear in mind that WP_DEBUG limits the performance of WP_DEBUG_LOG or WP_DEBUG_DISPLAY. WP_DEBUG needs to be enabled for any of the other two to work. Don&#8217;t forget to disable them once you are done fixing the website! While WP_DEBUG_DISPLAY is active, an error message or a warning may leak sensitive data about the website.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Debugging_and_Troubleshooting_CSS_and_JS_Code\"><\/span>Debugging and Troubleshooting CSS and JS Code<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>If you are responsible for the day to day maintenance of a WordPress website, knowing how to troubleshoot PHP errors will probably be enough for you. However, if you are also working on theme and\/or plugin design, you might need a little more help.<\/p>\n\n\n\n<p>By default, WordPress will serve minified versions of its common CSS and JS libraries, and will also concatenate scripts to improve loading speed a little. Both of these measures make the job of a troubleshooter much more difficult. Thankfully, WordPress is giving us two functions to fix this.<\/p>\n\n\n\n<p>SCRIPT_DEBUG will serve the non-minified versions of all JS and CSS files for the front end (wp-includes\/js, wp-includes\/css) and back-end (wp-admin\/js, wp-admin\/css) to make it easier for developers and testers to view the code from the browser console.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/* \n* Uses non-minified versions of wp-includes\/js, wp-includes\/css,\n* wp-admin\/js, and wp-admin\/css instead of .min.css and .min.js\n*\/\ndefine( 'SCRIPT_DEBUG', true );<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/*\n* Does not concatenate scripts to improve\n* browsing and back\/forth scrolling\n*\/\ndefine( 'CONCATENATE_SCRIPTS', false );<\/code><\/pre>\n\n\n\n<p>Sidenote: If you are using a modern web server that supports HTTP\/2 or later, it actually makes sense to keep CONCATENATE_SCRIPTS permanently disabled. Resource concatenation was a great way to improve site loading speed in olden times when browsers were allowed to open a limited number of sessions to the web servers, but these days concatenation might actually do more harm than good.<\/p>\n\n\n\n<p>For one thing, concatenating different sets of scripts for different web pages degrades caching performance. It also increases the overall amount of traffic (and wastes bandwidth). And last but not least, variations in concatenation sequences might make some websites inoperable (if a specific bit of JS code gets loaded before a related bit of JS code is forcefully located at the back of the concatenated file).<\/p>\n\n\n\n<p>For these reasons, most modern websites have all but given up concatenating scripts. So you might just as well test to see any improvement.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Cookie_Control\"><\/span>Cookie Control<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>WordPress gives you the ability to limit the scope of cookies created by the platform. You can restrict them only to the apex domain, or a subdomain, or make them accessible for the apex domain and all subdomains.<\/p>\n\n\n\n<p>Unless you have a very specific use case \u2014 like content posted on a sub-domain that is visible to users logged in with a cookie on the main site \u2014 it is best to restrict cookies to be valid only on the domain or sub-domain where the WordPress instance is located. Among other things, this prevents the server from sending cookies back when serving requests for static resources fetched via CDN, which saves bandwidth.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'COOKIE_DOMAIN', '.domain.com' ); \/\/ Domain and all subdomains\ndefine( 'COOKIE_DOMAIN', 'domain.com' ); \/\/ only root domain\ndefine( 'COOKIE_DOMAIN', 'www.domain.com' ); \/\/ only subdomain<\/code><\/pre>\n\n\n\n<p>This is how to store the site name and path inside the cookies.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'COOKIEPATH', $_SERVER&#91;'HTTP_HOST'] . '\/' ); \/\/ You should set this explicitely.\ndefine( 'SITECOOKIEPATH', $_SERVER&#91;'HTTP_HOST'] . '\/' ); \/\/ You should set this explicitely.\ndefine( 'ADMIN_COOKIE_PATH', SITECOOKIEPATH . 'wp-admin' );\ndefine( 'PLUGINS_COOKIE_PATH', preg_replace( '|https?:\/\/&#91;^\/]+|i', '', WP_PLUGIN_URL ) );<\/code><\/pre>\n\n\n\n<p>And this is how you can control the naming of your cookies:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'USER_COOKIE', 'wordpressuser_' . COOKIEHASH );\ndefine( 'PASS_COOKIE', 'wordpresspass_' . COOKIEHASH );\ndefine( 'AUTH_COOKIE', 'wordpress_' . COOKIEHASH );\ndefine( 'SECURE_AUTH_COOKIE', 'wordpress_sec_' . COOKIEHASH );\ndefine( 'LOGGED_IN_COOKIE', 'wordpress_logged_in_' . COOKIEHASH );\ndefine( 'RECOVERY_MODE_COOKIE', 'wordpress_rec_' . COOKIEHASH );<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Enable_Web_Access_for_Local_WordPress_Instance\"><\/span>Enable Web Access for Local WordPress Instance<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Local development is becoming very popular, as it allows the user to work on a WordPress instance that runs on their PC and does not require hosting or even internet access.<\/p>\n\n\n\n<p>However, even during local development, it might be necessary to allow that instance to connect to the web to download updates or for testing purposes. WordPress can use a proxy to connect to the Internet, and this is how you can instruct it to access that proxy.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_PROXY_HOST', '192.168.0.1' );\ndefine( 'WP_PROXY_PORT', '8000' );\ndefine( 'WP_PROXY_USERNAME', '' );\ndefine( 'WP_PROXY_PASSWORD', '' );\ndefine( 'WP_PROXY_BYPASS_HOSTS', 'localhost') ;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Database_Repair_Maintenance\"><\/span>Database Repair &amp; Maintenance<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>WordPress has an automated function for repairing and optimizing database tables. You can open it via <code>\/wp-admin\/maint\/repair.php<\/code> but in order to enable it, you need to add the following setting:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_ALLOW_REPAIR', 'true' );<\/code><\/pre>\n\n\n\n<p>This is not a forensic tool and you can&#8217;t expect miracles from it. It relies entirely on the repair functionality built into MySQL. But unless something really bad has happened, it can likely repair a broken table or two.<\/p>\n\n\n\n<p>Once done, you should either switch WP_ALLOW_REPAIR to false or delete the statement from the config file altogether.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"3rd_Party_Plugin_Functions\"><\/span>3rd Party Plugin Functions<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>You should know that 3rd party plugins might also choose to take advantage of wp-config.php to store their own configuration data.<\/p>\n\n\n\n<p>If you are using Jetpack or Akismet, you might add your WordPress.com API key to wp-config.php:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WPCOM_API_KEY', 'YourKeyHere' );<\/code><\/pre>\n\n\n\n<p>Under Apache, the popular object cache plugin Redis uses wp-config to specify things like host\/port combination, database number, key prefix, and access password.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>define( 'WP_REDIS_HOST', '127.0.0.1' );\ndefine( 'WP_REDIS_PORT', 6379 );\ndefine( 'WP_REDIS_PASSWORD', 'secret' );\ndefine( 'WP_REDIS_TIMEOUT', 1 );\ndefine( 'WP_REDIS_READ_TIMEOUT', 1 );<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/github.com\/rhubarbgroup\/redis-cache\/wiki\/Connection-Parameters\" target=\"_blank\" rel=\"noopener noreferrer\">Full list of Redis settings<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Keeping_wp-configphp_Secure\"><\/span>Keeping wp-config.php Secure<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>As a reward for getting to the very end, I will share with you one final hardening trick.<\/p>\n\n\n\n<p>On most web hosts, you can move wp-config.php outside the public_html directory. PHP will still be able to read the file from there, but the web server will be incapable of accessing it.<\/p>\n\n\n\n<p>In the very rare, but not unheard of situation where the PHP handler goes bust due to misconfiguration, the server will push the contents of regular PHP files as text on the screen instead of passing them to PHP for execution.<\/p>\n\n\n\n<p>Moving wp-config.php away from the reach of the web server guarantees that the important information (database name, db user name and db password) stays protected.<\/p>\n\n\n\n<p>At present, WPX does not allow wp-config.php to be moved away from its original location. Instead, they have implemented a server rule that blocks access to it via .htaccess.<\/p>\n\n\n\n<p>Go ahead and try that yourself: you will reach a WPX error page.<\/p>\n\n\n\n<p>In case your own web host does not offer such protection by default, you can block access to wp-config.php by adding the following lines to the .htaccess file in your root site folder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>&lt;files wp-config.php>\n order allow,deny\n deny from all\n &lt;\/file<\/code><\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Recap\"><\/span>Recap<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>These are only some of the possible constants you can use, and the ones that I find meaningful (there are many others I&#8217;ve left out as too obscure or inessential). Strangely, a full list of WordPress defined statements doesn&#8217;t seem to exist. I will try to keep this list current and will come back to update the article with new or improved information whenever I learn something new. Bookmark it and share it among your friends and colleagues, and if you know other useful settings, do share them in the comments below. Thanks!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The thing that motivates me in writing these columns for WPX.net is my desire to help you achieve mastery over WordPress. Mastery implies having knowledge and exerting control. There is one file within each WordPress installation that allows an inordinate amount of control over the behavior of the entire instance \u2014 core, themes, and even [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":2015371,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"cybocfi_hide_featured_image":"","footnotes":""},"categories":[86],"tags":[],"ppma_author":[111],"class_list":["post-2009895","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-wordpress"],"blocksy_meta":[],"authors":[{"term_id":111,"user_id":3,"is_guest":0,"slug":"ivan-arnaudov","display_name":"Ivan Arnaudov","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/168c2caf170cc2d43ef2ec57bc8af7347f86a5f5e825ee86026e52878df349b2?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/posts\/2009895","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/comments?post=2009895"}],"version-history":[{"count":7,"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/posts\/2009895\/revisions"}],"predecessor-version":[{"id":2016397,"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/posts\/2009895\/revisions\/2016397"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/media\/2015371"}],"wp:attachment":[{"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/media?parent=2009895"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/categories?post=2009895"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/tags?post=2009895"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/wpx.net\/blog\/wp-json\/wp\/v2\/ppma_author?post=2009895"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}