WordPress after installation: a 50-point checklist for 2026

WordPress after installation: a 50-point checklist for 2026

Last verified: October 13, 2026
22 min read
Guide
500+ WP projects
Security auditor

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);

Despite its name, the xmlrpc_enabled filter in point 2 only switches off XML-RPC methods that require authentication, such as publishing. Pingbacks and other unauthenticated methods still answer on xmlrpc.php. Blocking the whole file belongs on the web server, and only if nothing uses it: Jetpack, for example, connects through XML-RPC.

#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:

  1. Set WP_ENVIRONMENT_TYPE to production.
  2. Set DISALLOW_FILE_EDIT to true.
  3. Limit WP_POST_REVISIONS.
  4. Move WP_DEBUG_LOG to a private folder.
  5. Disable emoji/XML-RPC in code.

A well-configured WordPress instance is quiet, secure and fast.

Settings > Permalinks > Post name

Why:

  • Shorter, more readable URLs
  • Better for SEO (keywords in the URL)
  • Easier to remember and share

A fresh install often has pretty permalinks already. When the server supports URL rewriting, the installer sets /%year%/%monthnum%/%day%/%postname%/ (the core function wp_install_maybe_enable_pretty_permalinks()). Switch to Post name before the first post goes out: changing the structure on a live site changes every post URL, and the old addresses then need redirects.

#Category and tag structure

  1. Create the core categories (3-7 main ones)
  2. Define the hierarchy (parent and child categories)
  3. Set a default category (not “Uncategorized”)
  4. Plan a tag strategy (optional, 5-10 tags per post)

#WordPress media settings and image sizes

#Image sizes

Settings > Media

Recommended settings:

  • Thumbnail: 150x150px (tick “Crop thumbnail to exact dimensions”)
  • Medium: 300x300px
  • Large: 1024x1024px
  • Full size: always available

#Automatic image optimisation

<?php
/* Plugin Name: Image Sizes */

add_action( 'init', function () {
    remove_image_size( '1536x1536' ); // WordPress 5.3+
    remove_image_size( '2048x2048' ); // WordPress 5.3+
} );

Two lines common in copies of this snippet are left out on purpose. wp_lazy_loading_enabled already returns true by default (native loading="lazy" since WordPress 5.5), and the wp_image_editors filter only sets the order in which WordPress tries Imagick and GD, which is the default order anyway. Neither optimises anything at upload. Images wider or taller than 2560 px are already scaled down on upload through the big_image_size_threshold filter.

#How to enable WebP uploads in WordPress

add_filter( 'image_editor_output_format', function ( $formats ) {
    $formats['image/jpeg'] = 'image/webp';
    $formats['image/png']  = 'image/webp';
    return $formats;
} );

The snippet does not use wp_upload_image_mime_transforms, a filter that is not in current WordPress core. image_editor_output_format has existed since WordPress 5.8 and maps a source type to the type WordPress saves generated sizes in; since 6.7 its default already maps HEIC and HEIF to JPEG, and the snippet keeps those entries. The server’s Imagick or GD build has to support WebP. Tools > Site Health > Info > Media Handling shows what it supports.

#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):

// wp-config.php
$table_prefix = 'x7k9_';
RENAME TABLE wp_posts TO x7k9_posts;
RENAME TABLE wp_options TO x7k9_options;
-- ... repeat for every table

UPDATE x7k9_options SET option_name = 'x7k9_user_roles' WHERE option_name = 'wp_user_roles';
UPDATE x7k9_usermeta SET meta_key = CONCAT('x7k9_', SUBSTRING(meta_key, 4)) WHERE meta_key LIKE 'wp\_%';

WordPress has no DB_PREFIX constant: the prefix lives in the $table_prefix variable. Renaming the tables alone locks administrators out, because roles and capabilities are stored under prefixed keys in options (wp_user_roles) and usermeta (wp_capabilities, wp_user_level). That is what the two UPDATE statements fix. The Hardening WordPress guide treats a custom prefix as obscurity: it can block some automated SQL injection attacks that assume wp_, and it is no substitute for fixing injectable code.

#Protecting the wp-config.php file

# .htaccess (root, at the very top)
<Files "wp-config.php">
Require all denied
</Files>

Order allow,deny is Apache 2.2 syntax and only works on Apache 2.4 through the compatibility module. The Hardening WordPress guide gives the Require all denied form above and puts it at the very top of the root .htaccess. Nginx ignores .htaccess files, so on Nginx the same rule belongs in the server block.

#Blocking directory access

# wp-content/uploads/.htaccess
Options -Indexes

# .htaccess (root, outside # BEGIN WordPress and # END WordPress)
<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>

WordPress rewrites everything between the # BEGIN WordPress and # END WordPress markers whenever permalinks are saved, so this block goes outside them, in the root .htaccess, not in wp-includes/: the paths in the rules are relative to the site root. On multisite, the ^wp-includes/[^/]+\.php$ line stops ms-files.php from serving images, and the Hardening WordPress guide says to drop that line there, at the cost of some protection.

#Limiting login attempts

<?php
/* Plugin Name: Login Security */

function wppoland_login_key() {
    return 'login_attempts_' . md5( $_SERVER['REMOTE_ADDR'] );
}

add_filter( 'authenticate', function ( $user ) {
    if ( (int) get_transient( wppoland_login_key() ) >= 5 ) {
        return new WP_Error( 'too_many_attempts', 'Too many login attempts. Try again in an hour.' );
    }
    return $user;
}, 30 );

add_action( 'wp_login_failed', function () {
    $key = wppoland_login_key();
    set_transient( $key, (int) get_transient( $key ) + 1, HOUR_IN_SECONDS );
} );

remove_action( 'wp_head', 'wp_generator' );
add_filter( 'the_generator', '__return_empty_string' );

Counting failures in wp_login_failed and calling wp_die() there does not work. That hook runs after the password check, so a correct password still logged in after five failures. The authenticate filter at priority 30 runs after core has checked the username and password, and a WP_Error returned there stops the login. Behind a CDN or reverse proxy, REMOTE_ADDR holds the proxy’s address, so every visitor would share one counter: on such setups, limit logins at the proxy or with fail2ban on the server instead.

#Two-factor authentication (2FA)

WordPress core has no second factor. The Two Factor plugin, published on WordPress.org under the WordPress.org account, adds authenticator apps (TOTP), email codes and backup codes, and each user switches it on in their own profile. The Hardening WordPress guide recommends two-step authentication as an extra layer on top of the password. The snippet below does something smaller: it rejects passwords shorter than 12 characters when a profile is saved.

// 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

wp transient delete --expired
wp post list --post_type=revision --format=count
wp db optimize

The removed mu-plugin deleted transients by comparing option_value with the current time, but the expiry time sits in separate _transient_timeout_ rows, so it deleted the wrong rows. Core already does this job: wp-admin/admin.php schedules a daily delete_expired_transients event (skipped when a persistent object cache holds transients), and a daily wp_scheduled_auto_draft_delete event removes auto-drafts older than seven days. With WP_POST_REVISIONS set, revisions do not need a deletion cron either. Run the WP-CLI commands above by hand when a database looks bloated.

#Optimising the Heartbeat API

add_filter( 'heartbeat_settings', function ( $settings ) {
    $settings['interval'] = 120; // default: 60
    return $settings;
} );

add_action( 'init', function () {
    if ( ! is_admin() ) {
        wp_deregister_script( 'heartbeat' );
    }
}, 1 );

The Heartbeat default is 60 seconds (mainInterval in heartbeat.js), not 15, so the earlier value of 60 changed nothing. Heartbeat also slows itself to 120 seconds when the browser tab is hidden. In the editor it carries post locks, so a longer interval delays the warning that someone else is editing the same post.

// 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]
- Tagline: one short sentence, or empty

Settings > Reading:
- Search engine visibility: leave "Discourage search engines from indexing this site" UNCHECKED

Core prints no meta description, so the tagline is not one. On current installs the field starts empty, and themes may show it in the header or the title. The meta description comes from an SEO plugin or the theme. The search engine visibility checkbox lives under Reading, not General, and while it is checked WordPress also switches off its own sitemap.

Settings > Permalinks > Post name

#XML sitemap

add_filter( 'wp_sitemaps_post_types', function ( $post_types ) {
    unset( $post_types['portfolio'] ); // remove a post type from the sitemap
    return $post_types;
} );

The core sitemap at /wp-sitemap.xml has been on by default since WordPress 5.5, as long as the site is visible to search engines. It lists every public post type, custom ones included, so the old $post_types[] = 'portfolio' line was unnecessary and also wrong: the array holds WP_Post_Type objects keyed by name. The filter is useful the other way round, to take a type out of the sitemap.

#Basic image optimisation for SEO

A common snippet fills empty ALT attributes with the attachment title and generates titles from filenames. The second step is already core behaviour: media_handle_upload() takes the attachment title from the filename. The first one produces ALT text such as “IMG_2041”, which a screen reader announces and which says nothing about the picture. Write the ALT text per image in the media library and leave it empty for purely decorative images.

#How to back up WordPress

#Automatic database backups

# crontab -e (site user, not root)
15 3 * * * cd /var/www/example/public && wp db export /home/example/backups/db-$(date +\%F).sql --quiet && find /home/example/backups -name 'db-*.sql' -mtime +7 -delete

The earlier mu-plugin wrote dumps to wp-content/uploads/backups, a public folder where anyone who guesses the date-based filename can download the database. It also passed the database password on the mysqldump command line, where other users of the server can see it in the process list, and it relied on WP-Cron, which only runs when someone visits the site. A system cron job with wp db export writes outside the web root and runs on time.

#Setting up off-site backups

Recommended tools:

  1. UpdraftPlus: the free version is enough for small sites
  2. BlogVault: paid, but very reliable
  3. 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

add_action( 'admin_init', function () {
    if ( isset( $_GET['test_email'] ) && current_user_can( 'manage_options' ) ) {
        $sent = wp_mail( get_option( 'admin_email' ), 'Test email', 'This is a test message sent from WordPress.' );
        wp_die( $sent ? 'OK' : 'wp_mail() returned false' );
    }
} );

admin_init also fires on admin-ajax.php requests from visitors who are not logged in, so without the capability check anyone could make the site send mail. With WP-CLI the same test is one line: wp eval 'var_dump( wp_mail( get_option( "admin_email" ), "Test", "Test" ) );'.

#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
  • 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 # returns the "Already Installed" page unless the server blocks it
curl -I https://your-site.com/wp-config.php # 403 with the <Files> rule; without it, an empty page

#WordPress settings to change after installation

Three screens under Settings hold defaults that matter before the first visitor arrives. The General screen documentation covers each field.

General. Pick the timezone as a city rather than a UTC offset. A city follows daylight saving time, a fixed offset does not, and scheduled posts then publish an hour off for half the year. The site language set here is the language of the dashboard. A new administration email address does not take effect at once: WordPress sends a confirmation link to the new address and keeps the old one active until the link is clicked, so use a mailbox someone reads. Leave Membership (“Anyone can register”) off unless the site needs open registration, and if it does, keep the new user default role at Subscriber.

Reading. This screen holds the search engine visibility checkbox. Staging copies should have it checked and production must not. The setting is stored in the database, so it travels with every copy of the database: check it after each move from staging to production.

Discussion. A new install has comments and pingbacks open by default, manual approval of comments switched off and “Comment author must have a previously approved comment” switched on. On a company site without comments, close them here before publishing. The setting applies to new posts; existing posts keep their own status.

#How to read Site Health after installing WordPress

Tools > Site Health runs a set of checks and sorts the results into critical issues and recommended improvements (Site Health screen documentation). On a new site, the useful checks are the ones the settings in this article touch:

  • Debugging enabled. With WP_DEBUG_LOG on, the check turns critical when the PHP error log sits inside the WordPress directory, because the log can then be downloaded. That is why the log path earlier in this article points outside the public folder.
  • Search engine visibility. Flagged while “Discourage search engines” is checked (a test added in WordPress 6.9).
  • Open registration with privileged default role. Flagged when anyone can register with a role above Subscriber (since WordPress 7.0).
  • Background updates, plugin and theme auto-updates, scheduled events, loopback request. If these fail, automatic updates and WP-Cron jobs do not run.
  • HTTPS status, persistent object cache, page cache. Hosting checks. The two cache checks are recommendations, not errors.

The Info tab lists server details (PHP version and extensions, database version, image editor support, constants such as WP_DEBUG_LOG and WP_ENVIRONMENT_TYPE) and has a button that copies everything to the clipboard. Pasting that into a ticket saves a round of questions with the host.

#How automatic updates work on a new WordPress site

There are three layers, set in different places (Configuring automatic background updates):

  • Minor core releases (security and maintenance) install automatically, and have done since WordPress 3.7.
  • Major core releases. On sites installed with WordPress 5.6 or later, the auto_update_core_major option starts as enabled. Sites upgraded from an older version start with it unset. The WP_AUTO_UPDATE_CORE constant overrides both: true for all releases, 'minor' for minor releases only, false to switch background core updates off.
  • Plugins and themes. Auto-updates are off by default and are switched on per plugin or theme on the Plugins and Themes screens, a feature added in WordPress 5.5.

DISALLOW_FILE_MODS (point 5 of the checklist) does more than hide the install and update buttons. Core checks wp_is_file_mod_allowed( 'automatic_updater' ) before any background update, so the constant also stops automatic security releases. That is fine when every update ships through a deployment. Without such a process, the site stays on the version it was deployed with until someone deploys again.

#Should you disable application passwords in WordPress

Application passwords, added in WordPress 5.6, let an external tool authenticate to the REST API with the username and a generated password instead of the account password (Application Passwords documentation). Each user creates and revokes them in their profile. A new password is shown once, stored as a hash, and listed with its last use date and IP address. Core offers the feature only on HTTPS sites or when WP_ENVIRONMENT_TYPE is local.

Leave it on when a mobile app, a deployment tool or an integration talks to the REST API. On a site with none of those, one filter turns it off for every user:

add_filter( 'wp_is_application_passwords_available', '__return_false' );

After a suspected break-in, revoke application passwords as well as rotating the salts. New salts end all login sessions, but application passwords are hashed with a fixed key that does not depend on the salts, so they keep working until someone revokes them.

#How to set up the privacy policy page in WordPress

The installer creates a “Privacy Policy” page as a draft and assigns it on Settings > Privacy, a screen added in WordPress 4.9.6 (Privacy settings documentation). The draft holds suggested headings and sample text, not a finished policy. The same screen lets you pick another page and links to the privacy policy guide, where core and plugins that register suggested text through wp_add_privacy_policy_content() describe what they store.

Two things follow. The draft is not public until someone edits and publishes it. And themes print the policy link through the_privacy_policy_link(), which outputs nothing while the assigned page is unpublished, so a footer link that “is missing” usually means an unpublished page. Tools > Export Personal Data and Tools > Erase Personal Data handle access and erasure requests for the data WordPress and cooperating plugins keep.

#Summary: the 50-point checklist

#Security (1-15)

  1. ✅ Set a custom table prefix (on install, via $table_prefix)
  2. ✅ Set strong passwords (min. 12 characters)
  3. ✅ Change the admin username
  4. ✅ Disable the file editor in the admin
  5. ✅ Block plugin installation (optional; also stops automatic updates)
  6. ✅ Force SSL in the admin panel
  7. ✅ Protect wp-config.php with .htaccess
  8. ✅ Block directory access (no indexes)
  9. ✅ Restrict XML-RPC (the filter covers authenticated methods only; keep it for Jetpack)
  10. ✅ Hide the WordPress version
  11. ✅ Limit login attempts
  12. ✅ Set up two-factor authentication (Two Factor plugin)
  13. ✅ Rotate the authentication keys (salts) regularly
  14. ✅ Install an SSL certificate
  15. ✅ Configure server-level protection (firewall, login attempt limits)

#Performance (16-25)

  1. ✅ Limit post revisions (5-10)
  2. ✅ Disable emoji
  3. ✅ Disable unused features (XML-RPC, RSD)
  4. ✅ Optimise image sizes
  5. ✅ Confirm lazy loading is active (core default since 5.5)
  6. ✅ Configure caching (object cache, page cache)
  7. ✅ Optimise the Heartbeat API
  8. ✅ Enable GZIP/Brotli compression
  9. ✅ Configure a CDN (optional)
  10. ✅ Optimise the database (automatic cleanup)

#SEO (26-35)

  1. ✅ Set the “Post name” permalink structure
  2. ✅ Set the site title and tagline (the tagline is not a meta description)
  3. ✅ Check the core XML sitemap at /wp-sitemap.xml
  4. ✅ Add the site to Google Search Console
  5. ✅ Configure Google Analytics / GA4
  6. ✅ Write ALT text per image
  7. ✅ Create the category structure
  8. ✅ Configure breadcrumbs
  9. ✅ Add the Organization schema
  10. ✅ Prepare Open Graph meta tags

#Backups (36-42)

  1. ✅ Set up automatic database backups
  2. ✅ Set up weekly full backups
  3. ✅ Store backups in an external location
  4. ✅ Test restoring from a backup
  5. ✅ Set backup retention (min. 30 days)
  6. ✅ Document the restore procedure
  7. ✅ Verify backup integrity regularly

#Content and functionality (43-50)

  1. ✅ Create the core pages (About us, Contact, Privacy policy)
  2. ✅ Configure the navigation menus
  3. ✅ Test every form
  4. ✅ Configure SMTP for email
  5. ✅ Create a custom 404 page
  6. ✅ Add a favicon and PWA icons
  7. ✅ Run performance tests
  8. ✅ 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: staging on the test copy, production on the live site. Every condition built on wp_get_environment_type() depends on this value.
  • WP_DEBUG: on in staging, off in production. WP_DEBUG_DISPLAY stays false in 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.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Frequently asked questions

Practical answers to apply the topic in real execution.

Should I change the default database table prefix?#
It helps a little. The Hardening WordPress guide on developer.wordpress.org lists a custom prefix under obscurity: it blocks some automated SQL injection attacks that assume 'wp_', and does nothing against injectable code itself. Set it during installation through the $table_prefix variable in wp-config.php. Changing it later means renaming every table and updating the prefixed keys in the options and usermeta tables, otherwise administrators lose their roles.
How often should I back up WordPress?#
For active sites: daily database backups and weekly full backups. For sites that change often (shops, blogs): real-time backups or every few hours. For simple brochure sites, weekly backups are enough. Always keep backups in an external location (cloud storage, another server).
Will disabling XML-RPC affect how my site works?#
Usually not, with two exceptions. Jetpack needs xmlrpc.php to connect, and its known-issues page says plugins that restrict that file cause problems. Also, the xmlrpc_enabled filter only switches off methods that require authentication, so pingbacks still go through xmlrpc.php. To close the file completely, block it on the web server.
How many post revisions should I keep?#
5-10 revisions are recommended. That lets you restore earlier versions without bloating the database. Sites that rarely change can use 3-5, active blogs 10-15. Never disable revisions completely: they are your safety net against losing content.
Do I need a caching plugin right after installation?#
Basic optimisation (disabling features you do not need, optimising images) is enough to start with. A caching plugin is worth adding once the site starts getting traffic. For small sites, focus on hosting quality first: good hosting with LiteSpeed or Nginx often delivers enough performance without extra plugins.

Need an FAQ tailored to your industry and market? I can build one aligned with your business goals.

Let’s discuss

Related Articles