How to reduce high CPU usage on a VPS running Nginx and PHP-FPM for multiple WordPress sites? [closed]

I have a VPS with 4 vCPUs, 8 GB RAM, running Ubuntu 22.04, Nginx, PHP-FPM (v8.1), and MariaDB 10.6. It hosts around 10 WordPress sites.

During peak traffic, I notice CPU usage spikes to 95–100%, and the sites start slowing down.

Things I’ve already tried:

Enabled Nginx microcaching

Increased PHP-FPM pm.max_children to 20

Configured MariaDB with basic tuning (innodb_buffer_pool_size, query_cache_size)

But the performance issue still persists.

Question:
What server-level optimizations (Nginx, PHP-FPM, or MySQL/MariaDB tuning) are recommended to reduce CPU usage in this scenario? I’m looking for specific configuration changes or diagnostic steps, not general “use caching/CDN” advice.

What I tried:

Increased pm.max_children in PHP-FPM to 20 expecting it to handle more requests in parallel.

Enabled Nginx microcaching expecting reduced PHP load.

Tuned MariaDB (innodb_buffer_pool_size, query_cache_size) expecting queries to run faster.

What I expected:

CPU usage should drop significantly.

WordPress sites should respond faster under load.

What actually happened:

CPU usage still spikes to 95–100% during peak traffic.

Response times are only slightly better but still too slow when concurrent users increase.

How to dynamically detect the active page builder in use on a WordPress page

I am developing a WordPress plugin to analyze which page builder (e.g., Elementor, WPBakery, Divi) is actively being used to render the current page, not just which builder plugins are installed and active on the site which is similar to wappalyzer and builtwith.

My current approach is flawed because it only checks a pre-defined list of plugins against the active plugins list. This tells me if a builder is installed, but not if it was actually used to build this specific page.

private function analyze_builders()
{
    $all_plugins = get_plugins();
    $builders = [];

    $builder_list = [
        'elementor/elementor.php' => 'Elementor',
        'wpbakery-visual-composer/wpbakery.php' => 'WPBakery',
        'divi-builder/divi-builder.php' => 'Divi Builder',
        // ... other builders
    ];

    foreach ($builder_list as $slug => $label) {
        if (isset($all_plugins[$slug])) {
            $builders[] = [
                'name' => __($label, 'pluginbuilder'),
                'status' => is_plugin_active($slug) ? __('Active', 'pluginbuilder') : __('Inactive', 'pluginbuilder')
            ];
        }
    }

    if (empty($builders)) {
        return [
            [
                'name' => __('No builder used', 'pluginbuilder'),
                'status' => ''
            ]
        ];
    }

    return $builders;
}

The Problem:
This method fails in two key scenarios:

  1. If a site has multiple page builders active (e.g., both Elementor and WPBakery), it returns both, but doesn’t tell me which one built this page.

  2. If a page builder is used that is not on my pre-defined list, it returns “No builder,” which is incorrect.

What I Need:
I need a way to dynamically detect the page builder from the page content itself. I’m looking for a reliable method to check the current post’s metadata or content for tell-tale signs of a specific page builder.

My Research & Ideas:
I’ve researched and believe the solution might involve checking for builder-specific patterns, such as:

  • Post Meta Data: Checking the _wp_page_template meta key or builder-specific keys like _elementor_edit_mode or _wpb_shortcodes_custom_css.

  • Content Analysis: Scanning the post content for shortcodes ([vc_row]) or HTML comments (<!-- /wp:shortcode -->) and CSS classes specific to a builder.

  • Database Queries: Perhaps performing a specific database query on the postmeta table for the current post ID.

My Question:
What is the most robust and performant method to detect which page builder was used for the current WordPress page? I am particularly interested in hooks, filters, or database queries that are unique to major page builders like Elementor, WPBakery, and Divi.

Example of desired output:
For a page built with Elementor, the function should return 'Elementor'.
For a classic page with no builder, it should return false or 'None'.

My PHP http_response_code is not sending status 200 but status code 302?

Hi all I am facing an issue with my Webhook response code whereby I am sending a Http response code of 302 (redirect) based on my http server logs.
This 302 redirect is overriding my default HTTP response code 200 I have set up in my scripts.
The 302 redirect is coming from the require_once in my main script below but it is after my http_response_code(200).
Any suggestion how do I ensure only my main script’s http_responce_code(200) is being sent out and not the require_once files.
We are using PHP version 5 on our end.
Code snippet as below:

  if (hash_equals($provided_signature, $yourHash)) {

    http_response_code(200);

    if ($product === 'v1'){
        $pymntGateway='curlec';
        require_once "v1_backend.php"; (302 redirect from here)
    }
    elseif ($product === 'v2'){
        $pymntGateway='curlec';
        require_once "v2_backend.php"; (302 redirect from here)
    }
    else{
        http_response_code(202);
        exit('Unknown product.');
    }

  }
  else{
      http_response_code(403); // Forbidden
    exit('Invalid signature.');
  }

I want to work with Postman and PHP without a database [closed]

I want to work with Postman and PHP without a database. I know this would all be temporary, but it’s just for testing. My goal is to create a temporary database in Postman with 5 to 10 records in JSON format. Then, I want to display that in a PHP API file and be able to insert, update, and delete records within that JSON from the PHP API file. Is this possible without using any kind of database (SQL and NoSQL databases, JSON file, Array, Session, or Cookie)? If yes, how? If no, why not?”

Here is my PHP API code:

<?php
// PHP Script to receive and display JSON data from Postman

// 1. Set the content type header.
// This tells Postman that the response will be in JSON format.
header('Content-Type: application/json');

// 2. Get the raw JSON data from the request body.
// This is the core function that receives data without a database.
$json_data = file_get_contents('php://input');

// 3. Decode the JSON string into a PHP array.
// This makes the data readable and usable in PHP.
$request_data = json_decode($json_data, true);

// 4. Check if data was received.
if ($request_data) {
    // 5. Create a response array with the received data.
    $response = [
        'status' => 'success',
        'message' => 'Data received and displayed successfully!',
        'received_data' => $request_data
    ];
} else {
    // If no data was received.
    $response = [
        'status' => 'error',
        'message' => 'No JSON data received or data is invalid.'
    ];
}

// 6. Encode the PHP array back to JSON and send it as the response.
echo json_encode($response, JSON_PRETTY_PRINT);
?>

Here is Postman result:

    {
        "status": "success",
        "message": "Data received and displayed successfully!",
        "received_data": {
            "user_id": 1,
            "product_name": "Laptop",
            "price": 1200
        }
    }

It still does not display in php file. That is my question – why?

Here is PHP file reault:

{
    "status": "error",
    "message": "No JSON data received or data is invalid."
}

Improving security at login using a file [closed]

A ( in the username and password method ) :

  1. The password is stored as a password_hash

  2. We retrieve the password hash using the username and verify it with password_verify

  3. And if the verification is successful, we update the same field ( cell ) again with a new password_hash.

B ( in the file method ) :

I have a table where the password column has a unique index, and the passwords are stored as hash('sha3-512', $_POST['password']). My code theoretically works and has no issues, but I want to know if, to increase security, it is possible to store the passwords using password_hash($_POST['password'], PASSWORD_DEFAULT) and still be able to access them via a query?

<input type="file" id="file">

<textarea style="display: block;width: 300px;height: 150px;" id="password"></textarea>

<script>
    document.getElementById("file").addEventListener("change", function (event) {

        const filereader = new FileReader();
        filereader.onload = function () {
            var filedata = filereader.result.split(',')[1];
            const datalength = filedata.length;
            filedata = filedata.slice(Math.round(((datalength * 2) / 9)) - 100, Math.round(((datalength * 2) / 9)) + 100) + filedata.slice(Math.round(((datalength * 5) / 9)) - 100, Math.round(((datalength * 5) / 9)) + 100) + filedata.slice(Math.round(((datalength * 8) / 9)) - 100, Math.round(((datalength * 8) / 9)) + 100);
            if (/^[a-zA-Z0-9+=/]*$/.test(filedata)) {
                document.getElementById('password').value = filedata;
            }
        };
        filereader.readAsDataURL(event.target.files[0]);
    });
</script>
<?php

//create by file :
if (!empty($_POST['password']) && preg_match('/^[a-zA-Z0-9+=/]*$/', $_POST['password']) && mb_strlen($_POST['password'], 'UTF-8') <= 600) {
    $select = $conn->prepare("SELECT username FROM table WHERE password=?");
    $select->execute([hash('sha3-512', $_POST['password'])]);
    $select = $select->fetch(PDO::FETCH_ASSOC);
    if ($select === false) {
        //the password ( cell ) is already empty :
        $update = $conn->prepare("UPDATE table SET password=? WHERE username=?");
        $update->execute([hash('sha3-512', $_POST['password']), $_POST['username']]);
        //create ...
    }
    $conn = null;
}

//login by file :
if (!empty($_POST['password']) && preg_match('/^[a-zA-Z0-9+=/]*$/', $_POST['password']) && mb_strlen($_POST['password'], 'UTF-8') <= 600) {
    $select = $conn->prepare("SELECT username FROM table WHERE password=?");
    $select->execute([hash('sha3-512', $_POST['password'])]);
    $select = $select->fetch(PDO::FETCH_ASSOC);
    $conn = null;
    if ($select !== false) {
        //login ...
    }
}

Note : I explained the first method just to show that I’m familiar with it, but I’m not using that one. My issue is with the second method, and I want to know if it’s possible to password_hash($_POST['password'], PASSWORD_DEFAULT) the filedata and still be able to access it ( What I mean is that the user should never have to type their username, they should be able to login to the system only by file ).

WP cron job is not triggering a custom plugin

I am working on a class-based plugin. I need a custom cron job that will be active when the plugin is active and run the plugin function. Now, I am facing an issue where it is not calling the plugin function, but I have checked the cron that has already been created. Below is my code. Let me know what I am missing here. smcp_cron_do_task() is not triggered by the cron job.

Also, I’m testing it in my local Docker system.

class ClassName {

    public function __construct() {
        // Ensure custom cron intervals are registered early
        add_filter( 'cron_schedules', array( $this, 'add_custom_intervals' ) );

        // Hooks
        register_activation_hook( __FILE__, array( $this, 'smcp_cron_activate' ));
        register_deactivation_hook( __FILE__, array( $this, 'smcp_cron_deactivate' ));

        add_action( 'smcp_cron_task_hook', array( $this, 'smcp_cron_do_task' ) );
    }

    // Schedule on activation
    function smcp_cron_activate() {
        if ( ! wp_next_scheduled( 'smcp_cron_task_hook' ) ) {
            wp_schedule_event( time(), 'every_minute', 'smcp_cron_task_hook' );
        }
    }

    // Clear scheduled event on deactivation
    function smcp_cron_deactivate() {
        $timestamp = wp_next_scheduled( 'smcp_cron_task_hook' );
        if ( $timestamp ) {
            wp_unschedule_event( $timestamp, 'smcp_cron_task_hook' );
        }
    }

    function smcp_cron_do_task() {
        // Custom Code
        error_log( 'My custom cron job ran at: ' . current_time('mysql') );
    }

    public function add_custom_intervals( $schedules ) {
        $schedules['every_minute'] = array(
            'interval' => 60,
            'display'  => __( 'Every Minute' ),
        );
        return $schedules;
    }
}

// Initialize plugin
new Site_Monitor();

I want to trigger this smcp_cron_do_task() by the cron job.

getting error while fetching data between two dates in Laravel, php or mysql

I does a big mistake i store date in varchar field in database, and when i try to use whereBetween function to get data it gives me annonoumus result due to string nature,

Can anyone tell me how to make this working without affecting database because i no longer have access to the database i can only change this on view

I tried this but this didn’t work ;

SELECT * 
FROM `cpds`
WHERE `user_id` = 21
  AND STR_TO_DATE(`date`, '%Y-%m-%d') BETWEEN STR_TO_DATE('2025-01-01', '%Y-%m-%d')
                                          AND STR_TO_DATE('2025-12-31', '%Y-%m-%d');

Need to create an Age Verification plugin for WordPress to connect to an external API [closed]

I’ve been asked to create an Adult Verification plugin to connect to an external API using Verifymy (https://verifymy.io/), they have some documentation, but I’m unsure how to build something to get people to age verify when people land on a website after they have signed up for an account.

Anyone have any working examples that I can look at/take inspiration from.

Any help would be absolutely awesome!

Many Thanks

Chris

extract with EXTR_PREFIX_ALL does not give me variables

I’m trying use extract() to create prefixed variables. However, the variable I expect is not being created, and I get an undefined variable warning.

Here’s my code:

<?php
    $my_user_id = 1;


    $stmt = $mysqli->prepare("SELECT id, email FROM users_index WHERE user_id=?"); 
    $stmt->bind_param("i", $my_user_id);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();
    $stmt->close();
    if (!$row) {
        echo "<p>There was no rows returned for your user session.</p>"; die;
    }
    extract($row, EXTR_PREFIX_ALL, 'sql_my_');

    if (empty($sql_my_id)) {
        echo "
        <p>PHP Error: The variable is blank. row['id']={$row['id']} sql_my_id=$sql_my_id.</p>
        <pre>";
        print_r($row);
        echo "</pre>";
        die;
    }

Output:

Warning: Undefined variable $sql_my_id in C:Apache24htdocswebsitetest.php

PHP Error: The variable is blank. row[‘id’]=1 sql_my_id=.

Array
(
[id] => 1
[email] => [email protected]
)

Problem:
I expected extract($row, EXTR_PREFIX_ALL, 'sql_my_') to create $sql_my_id and $sql_my_email, but $sql_my_id is not defined.

What I’ve tried:

  • Using EXTR_PREFIX_ALL with a prefix.
  • Checking if $row is empty before extracting.

Why isn’t $sql_my_id being created by extract()? How can I correctly create prefixed variables from my associative array?

autoformat with CS Fixer exited with non-zero code

I’m trying to set up PHP Coding Standards Fixer with PhpStorm but when I run the code auto formatter (Ctrl Alt L) I get an error:

PHP External Formatter: Process exited with non-zero code

error bubble

Clicking the notification doesn’t expands any info and I don’t know where to get this output or code.

I had set PHP CS Fixer as the external formatter in “Quality tools”, activated the inspection in “Quality tools > PHP CS Fixer” and the validate for “PHP CS Fixer path” does not show any issues.

I have also setted “Run built-in formatter before external formatter” in Advanced settings.

I have my .php-cs-fixer.dist.php not in the root of my project but in a symfony folder (so path is symfony/.php-cs-fixer.dist.php). Could this be the issue? How can I set this path if it is the case?

How can I fix this error? I would prefer not to ignore it…

Block style is not getting a priority over Font awesome icon

I have added a Font Awesome Icon to my WordPress website which should trigger on small screens such as mobile devices. But it looks like this

Image showing website

It is getting triggered even on larger screens. The problem is, this header was created as a custom block and registered in functions.php using the init hook. But the font awesome scripts are being registered using wp_enqueue_scripts() hook. I am guessing that since init hook is prioritised before wp_enqueue_scripts() hook, even when I have the styles to not show the icon on large screens, the icon styles since registered later gets a priority and shows up in on the screen. I was wondering if there was another way to register font awesome so that this would not happen.

This is my functions.php code

function create_block_temp_block_block_init() {
/**
 * Registers the block(s) metadata from the `blocks-manifest.php` and registers the block type(s)
 * based on the registered block metadata.
 * Added in WordPress 6.8 to simplify the block metadata registration process added in WordPress 6.7.
 *
 * @see https://make.wordpress.org/core/2025/03/13/more-efficient-block-type-registration-in-6-8/
 */
if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) ) {
    wp_register_block_types_from_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' );
    return;
}

/**
 * Registers the block(s) metadata from the `blocks-manifest.php` file.
 * Added to WordPress 6.7 to improve the performance of block type registration.
 *
 * @see https://make.wordpress.org/core/2024/10/17/new-block-type-registration-apis-to-improve-performance-in-wordpress-6-7/
 */
if ( function_exists( 'wp_register_block_metadata_collection' ) ) {
    wp_register_block_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' );
}
/**
 * Registers the block type(s) in the `blocks-manifest.php` file.
 *
 * @see https://developer.wordpress.org/reference/functions/register_block_type/
 */
$manifest_data = require __DIR__ . '/build/blocks-manifest.php';
foreach ( array_keys( $manifest_data ) as $block_type ) {
    register_block_type( __DIR__ . "/build/{$block_type}" );
}
}
add_action( 'init', 'create_block_temp_block_block_init' );

function university_files() {
wp_enqueue_style('custom-google-fonts', '//fonts.googleapis.com/css?family=Roboto+Condensed:300,300i,400,400i,700,700i|Roboto:100,300,400,400i,700,700i');
wp_enqueue_style('font-awesome', '//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css');
}

add_action('wp_enqueue_scripts', 'university_files');

In Inspector it’s showing that my icon styles with classname fa are getting priority over my own styles with classname site-header__menu-trigger

Image showing inspector tools

Here is my git repository for reference:
https://github.com/Revolter23/fictional-block-copy

Security issue with a WordPress plugin.plugin writes a log file to the uploads folder, which is publicly accessible via the browser on Nginx servers

I’m running into a security issue with a WordPress plugin. The plugin writes a log file to the uploads folder, which is publicly accessible via the browser on Nginx servers.

Originally, the log was saved to:

$log_file = WP_CONTENT_DIR . '/site.log';

But due to WordPress compliance and server permission issues (especially on shared hosts), we moved the log file to the uploads directory to ensure it’s writable. That worked — but now the file is exposed to the public, which is a security risk.

I don’t have access to the server config or Nginx rules, so I need to implement a fix within the plugin code itself.

Any suggestions on how to secure or hide the log file from direct access, while keeping it writable across different hosting environments?

supervisor creating logs file without the write permissions

We’re running some Laravel commands using Supervisor on our server. Each program writes to a log file in storage/logs.

The issue is that whenever a Supervisor rotates or creates a new log file, it’s being created with restrictive permissions -rw-r--r--.

What we need:

All log files should always be created with 0666 or 0777 permissions.

The log files should not be owned by root, but instead by the www-data user.

How can we configure Supervisor (or the environment) so that new log files created by these processes always use those permissions?

Example issue:

-rwxrwxrwx 1 root     root      5187 Aug 28 23:00 laravel-2025-08-28.log
-rwxrwxrwx 1 root     root      6780 Aug 29 23:00 laravel-2025-08-29.log
-rwxrwxrwx 1 root     root      7087 Aug 30 23:01 laravel-2025-08-30.log
-rwxrwxrwx 1 root     root      7295 Aug 31 23:00 laravel-2025-08-31.log
-rwxrwxrwx 1 root     root      8282 Sep  1 23:00 laravel-2025-09-01.log
-rwxrwxrwx 1 root     root      8282 Sep  2 23:00 laravel-2025-09-02.log
-rwxrwxrwx 1 root     root      6966 Sep  3 23:00 laravel-2025-09-03.log
-rwxrwxrwx 1 root     root      7295 Sep  4 23:00 laravel-2025-09-04.log
-rwxrwxrwx 1 root     root      9690 Sep  5 23:00 laravel-2025-09-05.log
-rwxrwxrwx 1 root     root      8533 Sep  6 23:00 laravel-2025-09-06.log
-rwxrwxrwx 1 root     root      7771 Sep  7 23:00 laravel-2025-09-07.log
-rwxrwxrwx 1 root     root      6801 Sep  8 23:00 laravel-2025-09-08.log
-rwxrwxrwx 1 root     root       574 Sep  9 14:54 laravel-2025-09-09.log
-rw-r--r-- 1 root     root       149 Sep 10 07:25 laravel-2025-09-10.log

Note, this one is created by the supervisor:

-rw-r--r-- 1 root     root       149 Sep 10 07:25 laravel-2025-09-10.log

MongoDB error: No suitable servers found (`serverSelectionTryOnce` set): [socket timeout calling hello on ‘localhost:27017’]. Topology type: Single

So I have a dockerized mongodb.

My docker-composer.yml code is:

  mongodb:
    image: mongo:latest
    container_name: mongodb
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: xxxx
      MONGO_INITDB_ROOT_PASSWORD: xxxx
    ports:
      - "27017:27017"
    volumes:
      - ./mongodb/data:/data/db
    logging:
      driver: "json-file"       # Use the JSON file logging driver
      options:
        max-size: "200k"        # Rotate log files when they reach 200KB
        max-file: "10"          # Keep up to 10 rotated log files

When I connect to it from with this command, it give me the list of databases:
docker exec -it mongodb mongosh “mongodb://wmd_mongo_admin21:T8d_jm5_y0Clz3De2x@localhost:27017/Test?authSource=admin” –eval “db.getCollectionNames()”

But when I try to connect to it using php driver:

$mongodb_url = "mongodb://xxxx:xxxx@localhost:27017/Test";

try {
    // Create MongoDB manager
    $manager = new MongoDBDriverManager($mongodb_url);

    // Test query on 'urls' collection
    $query = new MongoDBDriverQuery([]);
    $cursor = $manager->executeQuery('Test.urls', $query);
    
    echo "Mongodb successfully connected!n";

    foreach ($cursor as $doc) {
        print_r($doc);
    }
} catch (MongoDBDriverExceptionException $e) {
    echo "Failed to connect or query MongoDB. Error: " . $e->getMessage() . "n";
}

It give me this error:

Failed to connect or query MongoDB. Error: No suitable servers found (`serverSelectionTryOnce` set): [socket timeout calling hello on '127.0.0.1:27017']. Topology type: Single

The PHP driver works because I can create a MongoDB manager

$manager = new MongoDBDriverManager($mongodb_url);

The problem is when I try to execute a query

$query = new MongoDBDriverQuery([]);
$cursor = $manager->executeQuery('Test.urls', $query);

Please note that the php script was working perfectly and stop working suddently without me changing the code.

CodeSniffer rules override each other

I have those rules:

<rule ref="SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces">
    <include-pattern>/src/Module/Quotes/Adapter/Incoming/</include-pattern>
    <properties>
        <property name="namespacesRequiredToUse" type="array">
            <element value="AppModuleQuotesDomain"/>
            <element value="Psr"/>
        </property>
        <property name="allowUseFromRootNamespace" value="true"/>
    </properties>
</rule>

<rule ref="SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces">
    <include-pattern>/src/Module/DemandPartners/Adapter/Incoming/</include-pattern>
    <properties>
        <property name="namespacesRequiredToUse" type="array">
            <element value="AppModuleDemandPartnersDomain"/>
            <element value="Psr"/>
        </property>
        <property name="allowUseFromRootNamespace" value="true"/>
    </properties>
</rule>

It returns some messages that point issues in src/Module/Quotes/Adapter/Incoming/... files.

But when I reverse the order:

<rule ref="SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces">
    <include-pattern>/src/Module/DemandPartners/Adapter/Incoming/</include-pattern>
    <properties>
        <property name="namespacesRequiredToUse" type="array">
            <element value="AppModuleDemandPartnersDomain"/>
            <element value="Psr"/>
        </property>
        <property name="allowUseFromRootNamespace" value="true"/>
    </properties>
</rule>

<rule ref="SlevomatCodingStandard.Namespaces.UseOnlyWhitelistedNamespaces">
    <include-pattern>/src/Module/Quotes/Adapter/Incoming/</include-pattern>
    <properties>
        <property name="namespacesRequiredToUse" type="array">
            <element value="AppModuleQuotesDomain"/>
            <element value="Psr"/>
        </property>
        <property name="allowUseFromRootNamespace" value="true"/>
    </properties>
</rule>

It points to files in src/Module/DemandPartners/Adapter/Incoming directory.

It looks like the configuration at the bottom overwrites the configuration on the top.

How can I fix that? I would like to have different rules configurations for each directory.