Diagram illustrating the WordPress page request flow and potential performance bottlenecks on Rocky Linux.

How WordPress Actually Handles a Page Request (And Where Performance Is Lost)

, , ,

Series: WordPress Performance on DirectAdmin (Rocky Linux 9)

Phase 1: Foundations — Part 2 of 30

WordPress Performance on DirectAdmin (Rocky Linux 9), Part 2: Baseline Measurement & Profiling

This post is part 2 of 30 in the “WordPress Performance on DirectAdmin (Rocky Linux 9)” series (slug: wp-perf-da), within Phase 1: Foundations. In part 1 we focused on basic sanity checks; here we build a reproducible baseline and profiling workflow before deeper tuning in later phases.

Goals and scope

Before changing PHP-FPM pools, NGINX/Apache templates, or MySQL, you need:

  • A repeatable way to measure performance.
  • Clear visibility into PHP, web server, and WordPress behavior.
  • Per-domain baselines so you can compare before/after changes.

This article focuses on a single DirectAdmin-managed WordPress domain on Rocky Linux 9, but the same approach applies per domain.

1. Confirm environment and prerequisites

1.1 Identify DirectAdmin stack for the target domain

DirectAdmin can run:

  • Apache with mod_php or PHP-FPM
  • NGINX as reverse proxy in front of Apache
  • NGINX-only (less common in default installs)

On Rocky Linux 9, you should prefer PHP-FPM. Confirm what your target domain uses:

sudo grep -R "php-fpm" /usr/local/directadmin/data/users/*/httpd.conf 2>/dev/null
sudo grep -R "nginx_proxy" /usr/local/directadmin/data/users/*/nginx.conf 2>/dev/null

Then inspect the user and domain-specific configs (replace user and example.com):

sudo cat /usr/local/directadmin/data/users/user/httpd.conf
sudo cat /usr/local/directadmin/data/users/user/nginx.conf 2>/dev/null || echo "no nginx for this user"

Note whether the VirtualHost for example.com uses php-fpm handlers and whether NGINX is proxying to Apache.

1.2 Confirm PHP-FPM pool for the domain

DirectAdmin typically creates per-user or per-domain PHP-FPM pools under:

  • /usr/local/phpXY/etc/php-fpm.d/ (custom PHP builds), or
  • /etc/php-fpm.d/ (OS PHP, less common if using CustomBuild)

List pools:

sudo ls -1 /usr/local/php*/etc/php-fpm.d/ 2>/dev/null

Open the relevant pool (example for PHP 8.2, user user):

sudo sed -n '1,120p' /usr/local/php82/etc/php-fpm.d/user.conf

Record:

  • pm, pm.max_children, pm.max_requests
  • listen socket path or port
  • php_admin_value[error_log] if set

We’ll use these later when correlating load with PHP-FPM behavior.

2. Establish a clean WordPress performance baseline

2.1 Ensure WP-CLI is available

WP-CLI is essential for consistent testing. If not installed centrally, install under /usr/local/bin:

cd /tmp
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
sudo mv wp-cli.phar /usr/local/bin/wp
sudo chmod +x /usr/local/bin/wp

Test from the WordPress document root (e.g. /home/user/domains/example.com/public_html):

cd /home/user/domains/example.com/public_html
sudo -u user wp core version

2.2 Create a stable test page

For consistent benchmarks, avoid admin pages, logged-in sessions, and heavy plugins. Create or pick a simple public page (e.g. a static content page or a basic blog post).

  1. List pages:
cd /home/user/domains/example.com/public_html
sudo -u user wp post list --post_type=page --fields=ID,post_title,post_name,post_status --format=table
  1. If needed, create a “Perf Test” page:
sudo -u user wp post create \
  --post_type=page \
  --post_title="Perf Test" \
  --post_status=publish \
  --post_content="Baseline performance test page."
  1. Get the permalink:
sudo -u user wp post get <ID_FROM_ABOVE> --field=guid

Use that URL (or the pretty permalink) for all HTTP benchmarks in this phase.

3. Basic HTTP and TTFB measurements

3.1 Quick latency and TTFB with curl

Use curl from the same server to remove external network variance. Replace https://example.com/perf-test/ with your URL.

curl -o /dev/null -s -w \
'HTTP %{http_code}
Time_Namelookup %{time_namelookup}
Time_Connect %{time_connect}
Time_Appconnect %{time_appconnect}
Time_Pretransfer %{time_pretransfer}
Time_Starttransfer %{time_starttransfer}
Time_Total %{time_total}
' \
https://example.com/perf-test/

Key metrics:

  • Time_Starttransfer: effectively TTFB; includes PHP and DB time.
  • Time_Total: full response time.

Run it 5–10 times and note min/median/max. Store initial results (e.g. in a shared doc or Git-tracked notes) as your baseline.

3.2 Basic concurrency test with wrk

Install wrk (not in base Rocky 9 repos). From EPEL:

sudo dnf install -y epel-release
sudo dnf install -y wrk

Warning: Load generation can impact production users. Run during low-traffic windows and start with conservative settings.

Example low-intensity test:

wrk -t4 -c20 -d30s https://example.com/perf-test/
  • -t4: 4 threads
  • -c20: 20 concurrent connections
  • -d30s: 30 seconds duration

Capture:

  • Requests/sec
  • Latency distribution (50%, 75%, 90%, 99%)
  • Non-2xx/3xx responses (should be 0 for the test page)

Record these as your initial concurrency baseline.

4. Logging and visibility for profiling

4.1 Apache / NGINX access and error logs

DirectAdmin typically stores per-domain logs under the domain path. Common locations:

  • /var/log/httpd/domains/example.com.log
  • /var/log/httpd/domains/example.com.error.log
  • /var/log/nginx/domains/example.com.log
  • /var/log/nginx/domains/example.com.error.log

Confirm for your domain:

sudo ls -1 /var/log/httpd/domains
sudo ls -1 /var/log/nginx/domains 2>/dev/null || echo "no nginx domain logs"

During tests, tail logs in another terminal:

sudo tail -F /var/log/httpd/domains/example.com.log \
           /var/log/httpd/domains/example.com.error.log

If using NGINX reverse proxy, tail NGINX logs as well:

sudo tail -F /var/log/nginx/domains/example.com.log \
           /var/log/nginx/domains/example.com.error.log

Look for:

  • 5xx errors (500, 502, 503, 504)
  • Slow requests if you have custom log formats with request time (we’ll add this in a later phase)

4.2 PHP-FPM slow log (per pool)

PHP-FPM slow logs are critical for profiling WordPress bottlenecks.

Warning: Editing PHP-FPM pool configs triggers pool reload/restart. Brief disruption is possible. Do this during a low-traffic window.

Open the pool config (example for user user, PHP 8.2):

sudo vi /usr/local/php82/etc/php-fpm.d/user.conf

Add or adjust:

request_slowlog_timeout = 3s
slowlog = /var/log/php-fpm/user-slow.log

Create the log file and set permissions:

sudo touch /var/log/php-fpm/user-slow.log
sudo chown user:user /var/log/php-fpm/user-slow.log
sudo chmod 640 /var/log/php-fpm/user-slow.log

Reload the relevant PHP-FPM service (check service name using systemctl list-units | grep fpm):

sudo systemctl reload php-fpm82.service

Tail the slow log during wrk tests:

sudo tail -F /var/log/php-fpm/user-slow.log

Each entry will show the script and stack trace for requests exceeding 3 seconds, which often points to slow plugins, themes, or DB calls.

5. Baseline WordPress-level profiling

5.1 Check plugin and theme footprint

Use WP-CLI to see what’s active:

cd /home/user/domains/example.com/public_html
sudo -u user wp plugin list --status=active --fields=name,status,version
sudo -u user wp theme list --fields=name,status,version

For a baseline, consider temporarily disabling clearly non-essential heavy plugins (page builders, analytics, etc.) on a staging copy of the site to see the delta. In production, keep changes minimal and coordinated.

5.2 Measure page generation time from WordPress

Without installing heavy profiling plugins, you can add a short-lived mu-plugin to log generation time for the test page.

Warning: Code changes can break the site if misapplied. Test on staging first if possible.

Create wp-content/mu-plugins/perf-timer.php:

cd /home/user/domains/example.com/public_html
sudo -u user mkdir -p wp-content/mu-plugins
sudo -u user tee wp-content/mu-plugins/perf-timer.php >/dev/null <<'EOF'
<?php
add_action( 'template_redirect', function () {
    if ( ! defined( 'WP_START' ) ) {
        return;
    }
    $uri = $_SERVER['REQUEST_URI'] ?? '';
    // Adjust path to your test page if needed.
    if ( strpos( $uri, '/perf-test' ) !== 0 ) {
        return;
    }
    add_action( 'shutdown', function () {
        $elapsed = microtime( true ) - WP_START;
        error_log( sprintf( 'PERF_TEST page=%s time=%.4f', $_SERVER['REQUEST_URI'] ?? '', $elapsed ) );
    }, PHP_INT_MAX );
} );
EOF

This logs generation time to the PHP error log (often /var/log/httpd/domains/example.com.error.log or a pool-specific log). Hit the test URL and then:

sudo grep 'PERF_TEST' /var/log/httpd/domains/example.com.error.log | tail

Record typical values. Remove or comment out this mu-plugin after baseline collection to avoid log noise.

6. Good starting values for PHP-FPM and system limits

This phase is about measurement, but you should verify that your current PHP-FPM settings are not obviously broken before collecting baselines.

6.1 Check PHP-FPM pool limits

Open the pool config again:

sudo sed -n '1,160p' /usr/local/php82/etc/php-fpm.d/user.conf

For a typical small WordPress site (2–4 vCPU, 4–8 GB RAM) a reasonable starting point:

pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500

Warning: Misconfigured pm.max_children can cause 502/503 errors or memory exhaustion. Do not increase pm.max_children until you’ve measured memory usage under load (we’ll cover this in a later phase).

After changes:

sudo systemctl reload php-fpm82.service

6.2 Confirm systemd and firewalld state

Ensure services are healthy before and after tests:

sudo systemctl status httpd
sudo systemctl status nginx 2>/dev/null || echo "nginx not in use"
sudo systemctl status php-fpm82

Check firewall (if testing from external hosts):

sudo firewall-cmd --list-services

Make sure http and https are allowed:

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

7. DirectAdmin-specific templates and per-domain tuning hooks

DirectAdmin uses templates for Apache and NGINX vhosts. In later phases we’ll customize these for logging and caching; for now, just identify where they live so you know what’s safe to change.

7.1 Locate Apache and NGINX templates

Common template directories:

  • /usr/local/directadmin/data/templates/custom/ (preferred override location)
  • /usr/local/directadmin/data/templates/ (defaults; don’t edit directly if you can avoid it)

List templates:

sudo ls -1 /usr/local/directadmin/data/templates
sudo ls -1 /usr/local/directadmin/data/templates/custom 2>/dev/null || echo "no custom templates yet"

For Apache + NGINX proxy setups, you’ll usually see:

  • virtual_host2.conf / virtual_host2_secure.conf
  • nginx_server.conf / nginx_server_secure.conf

To customize safely, copy a template into the custom directory and then adjust it there. We’ll use this mechanism later for extended logging and caching headers.

7.2 Per-domain overrides

DirectAdmin supports per-domain custom configurations (e.g. Apache include files). For now, just note that for example.com you’ll see references in:

sudo grep -n 'example.com' /usr/local/directadmin/data/users/user/httpd.conf

In later phases, we’ll attach per-domain snippets (e.g. for SetEnvIf, Header, advanced logging) via these mechanisms instead of editing global configs.

8. Baseline checklist

Before moving on in the series, ensure you have:

  • Identified the stack for the domain (Apache-only vs NGINX+Apache, PHP-FPM pool in use).
  • Created or selected a stable public test page.
  • Captured curl timing metrics (TTFB and total time) for that page.
  • Run at least one low-intensity wrk test and recorded RPS and latency distribution.
  • Enabled and tailed:
    • Apache/NGINX access and error logs
    • PHP-FPM slow log for the relevant pool
  • Collected basic WordPress profiling data (active plugins/themes, optional perf-timer mu-plugin logs).
  • Verified PHP-FPM pool settings are within reasonable starting values.

These baselines allow you to prove that future tuning (e.g. PHP-FPM pool sizing, opcode cache changes, full-page caching) actually improves performance and doesn’t just shift bottlenecks.

This article offers general technical guidance. Validate all configurations in a safe environment before applying them to production.

Previous: WordPress Performance Tuning on DirectAdmin: The Complete Guide

Next: Common WordPress Performance Mistakes on VPS Hosting

Smart reads for curious minds

We don’t spam! Read more in our privacy policy

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *