The “famous 5-minute install” is a marketing slogan, not a professional standard. A default WordPress installation is “chatty”, unoptimised and often unsafe.
As developers we do not just “install” WordPress. We prepare it to run in a specific environment. This guide covers the key configuration constants and hardening techniques that belong in the baseline of every client project in 2026.
Configuring wp-config.php after installing WordPress
This is the brain of your installation. Stop leaving it on default settings.
Setting WP_ENVIRONMENT_TYPE in wp-config.php
Since WordPress 5.5, WP_ENVIRONMENT_TYPE is the standard. Use it to keep development errors from leaking into production.
// In wp-config.php
define( 'WP_ENVIRONMENT_TYPE', 'production' ); // 'local', 'development', 'staging', 'production'Then in your code:
if ( wp_get_environment_type() === 'production' ) {
// Enable cache, disable errors
}Disabling the file editor and forcing SSL in wp-config.php
Stop clients (or attackers) from breaking the site through the admin panel.
// Disable the file editor (theme/plugin editor)
define( 'DISALLOW_FILE_EDIT', true );
// Block plugin installs/updates (good for immutable deployments)
define( 'DISALLOW_FILE_MODS', true );
// Force SSL in the admin
define( 'FORCE_SSL_ADMIN', true );How to limit post revisions in WordPress
The database killer. Do you really need 100 versions of the “About us” page?
define( 'WP_POST_REVISIONS', 10 ); // Keep the last 10
// OR
define( 'WP_POST_REVISIONS', false ); // Disable completely (not recommended)How to enable WP_DEBUG_LOG without displaying errors
Never display errors on the frontend. Log them.
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/tmp/wp-errors.log' ); // Move the log outside the public directory!
define( 'WP_DEBUG_DISPLAY', false );
// Log SQL queries for performance debugging (disable in production!)
define( 'SAVEQUERIES', false );How to disable emoji and XML-RPC in WordPress
WordPress ships with features that 90% of business sites do not need: emoji, oEmbeds and XML-RPC.
Do not install a plugin to turn them off. Create a must-use plugin (wp-content/mu-plugins/lean-core.php).
<?php
/* Plugin Name: Lean Core */
// 1. Disable emoji (saves an HTTP request)
remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );
// 2. Disable XML-RPC (security)
add_filter( 'xmlrpc_enabled', '__return_false' );
// 3. Remove the WP version (security by obscurity)
remove_action( 'wp_head', 'wp_generator' );
// 4. Disable RSS feeds (if you are building a brochure site)
// function wppoland_disable_feed() {
// wp_die( 'No feed here, visit our homepage!' );
// }
// add_action('do_feed', 'wppoland_disable_feed', 1);Changing the salt keys in wp-config.php
You know the authentication keys in wp-config.php.
define('AUTH_KEY', 'put your unique phrase here');
// ...Fact: changing them logs every user out immediately. It is the “nuclear option” if the site has been hacked. Pro tip: automate their rotation with a CLI script or Vault if you manage enterprise sites.
What to set in wp-config.php before the site goes live
Before you launch:
- Set
WP_ENVIRONMENT_TYPEto production. - Set
DISALLOW_FILE_EDITto true. - Limit
WP_POST_REVISIONS. - Move
WP_DEBUG_LOGto a private folder. - Disable emoji/XML-RPC in code.
A well-configured WordPress instance is quiet, secure and fast.
Which permalinks to set in WordPress
The optimal permalink structure
Settings > Permalinks > Post nameWhy:
- Shorter, more readable URLs
- Better for SEO (keywords in the URL)
- Easier to remember and share
Configuration in wp-config.php:
// Force the permalink structure (optional)
define('WP_POST_REVISIONS', 10);Category and tag structure
- Create the core categories (3-7 main ones)
- Define the hierarchy (parent and child categories)
- Set a default category (not “Uncategorized”)
- Plan a tag strategy (optional, 5-10 tags per post)
WordPress media settings and image sizes
Image sizes
Settings > MediaRecommended settings:
- Thumbnail: 150x150px (tick “Crop thumbnail to exact dimensions”)
- Medium: 300x300px
- Large: 1024x1024px
- Full size: always available
Automatic image optimisation
// In a mu-plugin: wp-content/mu-plugins/image-optimization.php
<?php
/* Plugin Name: Image Optimization */
// Stop generating extra sizes you do not use
function remove_unused_image_sizes() {
remove_image_size('1536x1536'); // WordPress 5.3+
remove_image_size('2048x2048'); // WordPress 5.3+
}
add_action('init', 'remove_unused_image_sizes');
// Enable lazy loading for images
add_filter('wp_lazy_loading_enabled', '__return_true');
// Optimise images on upload
add_filter('wp_image_editors', function($editors) {
return ['WP_Image_Editor_Imagick', 'WP_Image_Editor_GD'];
});How to enable WebP uploads in WordPress
// Enable WebP (requires server support)
add_filter('wp_upload_image_mime_transforms', function($transforms) {
$transforms['image/jpeg'] = ['image/webp', 'image/jpeg'];
$transforms['image/png'] = ['image/webp', 'image/png'];
return $transforms;
});How to secure WordPress after installation
Changing the default table prefix
During installation: Enter a custom prefix instead of wp_ (for example x7k9_, mysite_, proj23_)
After installation (advanced):
-- Change the prefix in phpMyAdmin (take a backup first!)
RENAME TABLE wp_posts TO x7k9_posts;
RENAME TABLE wp_options TO x7k9_options;
-- ... repeat for all tables
-- Update wp-config.php
define('DB_PREFIX', 'x7k9_');Protecting the wp-config.php file
# In .htaccess (WordPress root directory)
<files wp-config.php>
order allow,deny
deny from all
</files>
# Extra protection
<files ~ "^.*\.([Hh][Tt][Aa])">
order allow,deny
deny from all
</files>Blocking directory access
# In wp-content/uploads/.htaccess
Options -Indexes
# In wp-includes/.htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>Limiting login attempts
// In a mu-plugin: wp-content/mu-plugins/login-security.php
<?php
/* Plugin Name: Login Security */
// Limit login attempts (without a plugin)
add_action('wp_login_failed', function($username) {
$ip = $_SERVER['REMOTE_ADDR'];
$attempts = get_transient('login_attempts_' . $ip);
if ($attempts === false) {
set_transient('login_attempts_' . $ip, 1, HOUR_IN_SECONDS);
} else {
set_transient('login_attempts_' . $ip, $attempts + 1, HOUR_IN_SECONDS);
if ($attempts >= 5) {
wp_die('Too many login attempts. Please try again in an hour.');
}
}
});
// Hide the WordPress version
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');Two-factor authentication (2FA)
// Enforce strong passwords
add_action('user_profile_update_errors', function($errors) {
$password = $_POST['pass1'];
if (!empty($password) && strlen($password) < 12) {
$errors->add('password_error', 'The password must be at least 12 characters long.');
}
});How to speed up WordPress after installation
Basic database optimisation
// In wp-config.php
// Limit post revisions
define('WP_POST_REVISIONS', 10);
// Empty the trash automatically after 7 days
define('EMPTY_TRASH_DAYS', 7);
// Disable the file editor (security + performance)
define('DISALLOW_FILE_EDIT', true);Database cleanup
// In a mu-plugin: wp-content/mu-plugins/db-cleanup.php
<?php
/* Plugin Name: DB Cleanup */
// Delete old revisions (keep the last 10)
add_action('wp_scheduled_auto_draft_delete', function() {
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->posts} WHERE post_type = 'revision' AND post_date < DATE_SUB(NOW(), INTERVAL 30 DAY)");
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_%' AND option_value < UNIX_TIMESTAMP()");
$wpdb->query("OPTIMIZE TABLE {$wpdb->posts}, {$wpdb->options}, {$wpdb->postmeta}");
});Optimising the Heartbeat API
// Reduce the Heartbeat frequency
add_filter('heartbeat_settings', function($settings) {
$settings['interval'] = 60; // seconds (default 15)
return $settings;
});
// Disable Heartbeat on the frontend
add_action('init', function() {
if (!is_admin()) {
wp_deregister_script('heartbeat');
}
}, 1);Removing RSD, shortlink and REST API links from wp_head
// In a mu-plugin: wp-content/mu-plugins/disable-bloat.php
<?php
/* Plugin Name: Disable Bloat */
// Disable emoji
remove_action('wp_head', 'print_emoji_detection_script', 7);
remove_action('wp_print_styles', 'print_emoji_styles');
// Disable XML-RPC
add_filter('xmlrpc_enabled', '__return_false');
// Disable the RSD link
remove_action('wp_head', 'rsd_link');
// Disable the Windows Live Writer link
remove_action('wp_head', 'wlwmanifest_link');
// Disable the shortlink
remove_action('wp_head', 'wp_shortlink_wp_head');
// Disable the REST API link (if you do not use it)
remove_action('wp_head', 'rest_output_link_wp_head');Basic SEO settings in WordPress
Site title, tagline and search engine visibility
Settings > General:
- Site title: [Your brand] | [Short description]
- Tagline: 2-3 sentences about the site (meta description)
- Search engine visibility: UNTICK "Discourage search engines from indexing this site"An SEO-friendly permalink structure
Settings > Permalinks > Post nameXML sitemap
// Enable the default WordPress sitemap
add_filter('wp_sitemaps_enabled', '__return_true');
// Add custom post types to the sitemap
add_filter('wp_sitemaps_post_types', function($post_types) {
$post_types[] = 'portfolio';
return $post_types;
});Basic image optimisation for SEO
// Automatic ALT attributes (fallback)
add_filter('wp_get_attachment_image_attributes', function($attributes, $attachment) {
if (empty($attributes['alt'])) {
$attributes['alt'] = get_the_title($attachment->ID);
}
return $attributes;
}, 10, 2);
// Image titles
add_filter('wp_insert_attachment_data', function($data, $postarr) {
if (empty($data['post_title'])) {
$filename = basename($data['guid']);
$data['post_title'] = sanitize_title($filename);
}
return $data;
}, 10, 2);How to back up WordPress
Automatic database backups
// In a mu-plugin: wp-content/mu-plugins/backup-scheduler.php
<?php
/* Plugin Name: Backup Scheduler */
// Daily database backup
add_action('daily_database_backup', function() {
$upload_dir = wp_upload_dir();
$backup_dir = $upload_dir['basedir'] . '/backups';
if (!file_exists($backup_dir)) {
wp_mkdir_p($backup_dir);
}
$filename = $backup_dir . '/db-backup-' . date('Y-m-d-H-i-s') . '.sql';
// Use WP-CLI or mysqldump
exec('mysqldump --user=' . DB_USER . ' --password=' . DB_PASSWORD . ' --host=' . DB_HOST . ' ' . DB_NAME . ' > ' . $filename);
// Delete old backups (keep the last 7)
$files = glob($backup_dir . '/db-backup-*.sql');
if (count($files) > 7) {
array_multisort(array_map('filemtime', $files), SORT_ASC, $files);
for ($i = 0; $i < count($files) - 7; $i++) {
unlink($files[$i]);
}
}
});
// Schedule the job
if (!wp_next_scheduled('daily_database_backup')) {
wp_schedule_event(time(), 'daily', 'daily_database_backup');
}Setting up off-site backups
Recommended tools:
- UpdraftPlus: the free version is enough for small sites
- BlogVault: paid, but very reliable
- WPvivid: a good free alternative
Configuration:
- Daily backup (database)
- Weekly backup (full)
- Retention: at least 30 days
- External location: Google Drive, Dropbox, S3
Configuring SMTP in WordPress
SMTP instead of mail()
// In wp-config.php or a mu-plugin
define('SMTP_USER', '[email protected]');
define('SMTP_PASS', 'your-password');
define('SMTP_HOST', 'smtp.gmail.com');
define('SMTP_PORT', '587');
define('SMTP_SECURE', 'tls');
define('SMTP_FROM', '[email protected]');
define('SMTP_NAME', 'Your Site');
// In a mu-plugin
add_action('phpmailer_init', function($phpmailer) {
$phpmailer->isSMTP();
$phpmailer->Host = SMTP_HOST;
$phpmailer->SMTPAuth = true;
$phpmailer->Username = SMTP_USER;
$phpmailer->Password = SMTP_PASS;
$phpmailer->SMTPSecure = SMTP_SECURE;
$phpmailer->Port = SMTP_PORT;
$phpmailer->From = SMTP_FROM;
$phpmailer->FromName = SMTP_NAME;
});How to send a test email from WordPress
// Test function (run once)
add_action('admin_init', function() {
if (isset($_GET['test_email'])) {
wp_mail(get_option('admin_email'), 'Test Email', 'This is a test message from WordPress.');
wp_die('Test email sent!');
}
});WordPress monitoring and error logging
Error logging
// In wp-config.php
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', '/var/log/wp-errors.log'); // Outside the public directory
define('WP_DEBUG_DISPLAY', false);
// SQL query logging (debug mode only)
// define('SAVEQUERIES', true);Uptime monitoring
Recommended tools:
- UptimeRobot (free plan: 50 monitors, 5-minute intervals)
- Pingdom (paid, but more advanced)
- Better Uptime (a modern alternative)
Logging admin logins and post updates
// In a mu-plugin: wp-content/mu-plugins/activity-log.php
<?php
/* Plugin Name: Activity Log */
add_action('wp_login', function($user_login, $user) {
error_log('User logged in: ' . $user_login . ' from IP: ' . $_SERVER['REMOTE_ADDR']);
}, 10, 2);
add_action('save_post', function($post_id, $post, $update) {
if ($update && $post->post_status === 'publish') {
error_log('Post updated: ' . $post->post_title . ' by user: ' . get_current_user_id());
}
}, 10, 3);What to check before launching a WordPress site
Pre-launch checklist
- All SEO settings correct
- Sitemap generated
- Google Search Console set up
- Google Analytics installed
- Privacy policy and terms added
- Contact forms tested
- Email works
- SSL works (HTTPS)
- Redirects between www and non-www configured
- Backup taken
- Caching plugins configured (optional)
- CDN configured (optional)
- Performance tests run (PageSpeed Insights)
- Mobile responsiveness tests
- Cross-browser tests
- 404 page configured
- Favicon added
Testing checksums and file access with WP-CLI and curl
# WP-CLI tests
wp core verify-checksums
wp plugin verify-checksums --all
wp db check
# Security tests
curl -I https://your-site.com/wp-admin/install.php # Should return 404
curl -I https://your-site.com/wp-config.php # Should return 403Summary: the 50-point checklist
Security (1-15)
- ✅ Change the database table prefix
- ✅ Set strong passwords (min. 12 characters)
- ✅ Change the admin username
- ✅ Disable the file editor in the admin
- ✅ Block plugin installation (optional)
- ✅ Force SSL in the admin panel
- ✅ Protect wp-config.php with .htaccess
- ✅ Block directory access (no indexes)
- ✅ Disable XML-RPC
- ✅ Hide the WordPress version
- ✅ Limit login attempts
- ✅ Set up two-factor authentication (2FA)
- ✅ Rotate the authentication keys (salts) regularly
- ✅ Install an SSL certificate
- ✅ Configure server-level protection (firewall, login attempt limits)
Performance (16-25)
- ✅ Limit post revisions (5-10)
- ✅ Disable emoji
- ✅ Disable unused features (XML-RPC, RSD)
- ✅ Optimise image sizes
- ✅ Enable lazy loading
- ✅ Configure caching (object cache, page cache)
- ✅ Optimise the Heartbeat API
- ✅ Enable GZIP/Brotli compression
- ✅ Configure a CDN (optional)
- ✅ Optimise the database (automatic cleanup)
SEO (26-35)
- ✅ Set the “Post name” permalink structure
- ✅ Configure the site title and tagline
- ✅ Enable the XML sitemap
- ✅ Add the site to Google Search Console
- ✅ Configure Google Analytics / GA4
- ✅ Optimise images (ALT attributes)
- ✅ Create the category structure
- ✅ Configure breadcrumbs
- ✅ Add the Organization schema
- ✅ Prepare Open Graph meta tags
Backups (36-42)
- ✅ Set up automatic database backups
- ✅ Set up weekly full backups
- ✅ Store backups in an external location
- ✅ Test restoring from a backup
- ✅ Set backup retention (min. 30 days)
- ✅ Document the restore procedure
- ✅ Verify backup integrity regularly
Content and functionality (43-50)
- ✅ Create the core pages (About us, Contact, Privacy policy)
- ✅ Configure the navigation menus
- ✅ Test every form
- ✅ Configure SMTP for email
- ✅ Create a custom 404 page
- ✅ Add a favicon and PWA icons
- ✅ Run performance tests
- ✅ Document the configuration
See our WordPress security audit if you want to get the site’s risks and protections in order.
wp-config.php differences between staging and production
This guide sets WP_DEBUG twice with different values: true in the debugging section and false in the monitoring section. Both are correct, each for a different environment, which is why the same copy of wp-config.php should not go unchanged to staging and to production. Before you move the file between servers, go through these constants:
WP_ENVIRONMENT_TYPE:stagingon the test copy,productionon the live site. Every condition built onwp_get_environment_type()depends on this value.WP_DEBUG: on in staging, off in production.WP_DEBUG_DISPLAYstaysfalsein both places, and the log goes outside the public directory.SAVEQUERIES: only while you are hunting slow queries, never in production.DISALLOW_FILE_MODS: in production it blocks installing and updating plugins from the admin, so updates go through deployment or WP-CLI. Agree this with the client before they notice the missing update button.- Authentication keys: generated randomly for every installation. A copy of production on staging gets its own set, and you leave the production keys alone, because changing them logs every user out.
After moving the file, run wp core verify-checksums and wp plugin verify-checksums --all from the pre-launch tests section.







