WordPress configuration

Below is our standard approach to configuring WordPress and key plugins.

See also details about WordPress including standard plugins.

WordPress core

Localisation:

  • Install English UK language
  • Set timezone to 'London'

Wordfence

Documentation: Wordfence Help

Installation

Perform the following steps in the dev environment:

  • Download the Wordfence plugin code (but don't activate the plugin yet)
  • Commit the plugin files to version control
  • Create a folder for log files
    mkdir src/wordpress/wp-content/wflogs
    
  • Add line to .gitignore to ignore this folder
    /src/wordpress/wp-content/wflogs
    
  • Add volume for log files to all three docker-compose.*.yml
    services:
      app:
        ...
        volumes:
            ...
            - wflogs:/var/www/html/wp-content/wflogs
            ...
    volumes:
      ...
      wflogs:
      ...
    
  • Restart the docker container with docker compose up -d
  • Set permissions for volume
    docker compose exec app chown civicrm:civicrm wp-content/wflogs
    
  • Activate the plugin
  • Get a license (use system@thirdsectordesign.org email)
  • Configure the firewall (Wordfence > Firewall > Manage WAF)
    • click Optimize Firewall
    • this enables extended protection
    • this will alter .htaccess and create wordfence-waf.php
  • Commit the changed files to version control - this should include
    src/wordpress/.htaccess
    src/wordpress/wordfence-waf.php
    .gitignore
    docker-compose.dev.yml
    docker-compose.prod.yml
    docker-compose.test.yml
    

Deployment

To deploy to test or prod follow the steps below - these can be copied and pasted into an issue.

- [ ] Merge and pull MR
- [ ] Run the following commands

  ```sh
  # Create log folder (if required)
  ls -al src/wordpress/wp-content
  mkdir src/wordpress/wp-content/wflogs

  # Restart container
  docker compose up -d

  # Set folder ownership
  docker compose exec app ls -al wp-content
  docker compose exec app chown civicrm:civicrm wp-content/wflogs

  # Activate plugin
  docker compose exec -u civicrm app wp plugin activate wordfence
  ```

  - [ ] Add license
  - [ ] Configure plugin
  - [ ] Put firewall into learning mode
  - [ ] Run a scan
  - [ ] Ask 3SD team to configure 2FA
  - [ ] Suggest client users configure 2FA

See below for details of how to configure this plugin

Configuration

Alter the setting below in Wordfence > All Options.

Wordfence Global Options

  • Wordfence license
    • copy license key from dev site (or create a new license key)
  • General Wordfence Options
  • Email Alert Preferences - select the following
    • Email me if Wordfence is deactivated
    • Email me if the Wordfence Web Application Firewall is turned off
    • Alert me with scan results of this severity level or greater: High
    • Alert when an IP address is blocked
    • Alert me when there's a large increase in attacks detected on my site
    • Maximum email alerts to send per hour = 10

Firewall

  • Basic Firewall Options
    • Put firewall into learning mode
    • Automatically enable in one week - choose other date if this isn't convenient (i.e. if you are going on holiday)
  • Brute Force Protection
    • Prevent the use of passwords leaked in data breaches = For all users with 'publish posts' capability (ensure that staff that can access CiviCRM have 'publish posts' capability)
    • Enforce strong passwords = Force all members to use strong passwords
    • Participate in the Real-Time Wordfence Security Network - disable by default

We should discuss with the client if they want to enable the last option. This has data protection implications as it sends IP addresses to Wordfence.

Login security

Change the following settings under Wordfence > Login Security > Settings

  • 2FA Roles
    • enable for each role that uses CiviCRM - set as 'optional' or 'required' as appropriate
  • Allow remembering device for 30 days = true

Configure 2FA for yourself and ask other 3SD staff to configure 2FA.

Scan

Run a scan on the site to check for any vulnerabilities.

WP Super Cache

Documentation: Initial WP Super Cache Setup and Configuration

Wiki: WP Super Cache

Preparation

Before configuring this plugin you should create a list of all pages that shouldn't be cached. This includes any pages that have CiviCRM content:

  • the CiviCRM base page
  • event pages
  • contribution pages
  • public CiviCRM forms - profiles or FormBuilder

We should add these pages to the list of 'Rejected URL Stings' in the advanced settings (see below). A simple way to do this is to ignore any URLs that contain the string 'civi'.

Installation and configuration

Perform the following steps in the dev environment:

  • Download the WP Super Cache plugin code (but don't activate the plugin yet)

  • Commit the plugin files to version control

  • Edit all three docker-compose.*.yml files to add the environment variables

    services:
      app:
        ...
        environment:
          ...
          - WP_CACHE=TRUE
          - WP_CACHE_SECRET
          - WP_CACHE_DEBUG_USERNAME
          - WP_CACHE_DEBUG_LOGFILE
    
  • Edit docker-compose.prod.yml and docker-compose.test.yml to add a cache volume

    services:
      app:
        ...
        volumes:
          ...
          - cache:/var/www/html/wp-content/cache
          ...
    volumes:
      ...
      cache:
      ...
    
  • Restart the docker container with docker compose up -d

  • Add a line to .gitignore to ignore cache files

    /src/wordpress/wp-content/cache
    
  • Ensure the wp-config.php file is writable (this is needed to activate the plugin but any changes made by the plugin should be reverted)

    docker compose exec -u civicrm app chmod +w wp-config.php
    
  • Create a wp-config-extra.php file with the following lines (or add to the existing file):

    <?php
    /**
    * Additional site specific WordPress config
    */
    
    /**
    * WP Super Cache settings
    */
    if ( getenv( 'WP_CACHE' ) === 'TRUE' ) {
      define( 'WP_CACHE', true );
      define( 'WPCACHEHOME', '/var/www/html/wp-content/plugins/wp-super-cache/' );
    }
    
  • Activate the plugin

  • Configure the advanced settings

    • Enable caching
    • Cache delivery method = Expert
    • Disable caching for logged in visitors = Yes
    • Compress pages so they’re served more quickly to visitors = Yes
    • Cache rebuild = Yes
    • Extra homepage checks = Yes
  • Rejected URL Strings - don't cache any forms - see above

    wp-.*\.php
    index\.php
    civi
    
  • Visit the debug settings page - this generates the debug log filename and username

  • The configuration settings are saved to wp-content/wp-cache-config.php

  • Copy secrets from wp-cache-config.php to .env

    WP_CACHE_SECRET=
    WP_CACHE_DEBUG_LOGFILE=
    WP_CACHE_DEBUG_USERNAME=
    
  • Edit the wp-cache-config.php file to use environment variables by replacing the secrets with the following lines - make sure you delete the existing variable definition so that these aren't overwritten

    $cache_page_secret       = getenv( 'WP_CACHE_SECRET' );
    $wp_cache_debug_log      = getenv( 'WP_CACHE_DEBUG_LOGFILE' );
    $wp_cache_debug_username = getenv( 'WP_CACHE_DEBUG_USERNAME' );
    
  • Restart the docker container with docker compose up -d

  • Commit the changed files to version control - this should include

    src/wordpress/wp-content/advanced-cache.php
    src/wordpress/wp-content/wp-cache-config.php
    src/wordpress/.htaccess
    src/wordpress/wp-config-extra.php
    .gitignore
    docker-compose.dev.yml
    docker-compose.prod.yml
    docker-compose.test.yml
    
  • Don't commit any changes to src/wordpress/wp-config.php. Any changes made to this file by activating the plugin should be reverted.

Testing

To check that caching is working visit the site in an incognito window (caching is disabled for logged in users). Check the bottom of the HTML for a comment like this:

<!-- Dynamic page generated in 0.112 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2024-12-04 09:32:40 -->
<!-- Compression = gzip -->

The time shouldn't change when you reload the page.

Pages that have been excluded from caching shouldn't have this comment at the bottom.

Check that public forms are working correctly (caching can break CSRF tokens). To test this fully, you need to test the page twice with a break of at least five minutes - the first time you submit the form it may work as it has been freshly created, the second time might not work as it is using an old cached token.

Deployment

To deploy to test or prod follow the steps below - these can be copied and pasted into an issue.

- [ ] Add secrets to `.env` - copy from dev
  ```env
  WP_CACHE_SECRET=
  WP_CACHE_DEBUG_USERNAME=
  WP_CACHE_DEBUG_LOGFILE=
  ```
- [ ] Merge and pull MR
- [ ] Run the following commands

  ```sh
  # Create cache folder (if required)
  ls -al src/wordpress/wp-content
  mkdir src/wordpress/wp-content/cache

  # Restart container
  docker compose up -d

  # Set folder ownership
  docker compose exec app ls -al wp-content
  docker compose exec app chown civicrm:civicrm wp-content/cache

  # Activate plugin
  docker compose exec -u civicrm app wp plugin activate wp-super-cache
  ```

Note: the configuration is committed to version control and will be read only on test and production. This means that it isn't possible to alter the settings via the UI.