Probably no one will mind that having a reliable and efficient web host is of paramount importance for both businesses and individuals involved in the Internet ...
3v-Hosting Blog
12 min read
For a long time, WordPress was perceived as a tool for quickly launching websites, because all you had to do was choose a suitable theme, install a couple of plugins, activate caching, and that was it - the project was up and running. But as traffic grows, it becomes clear that true performance is determined not by the PHP code of the theme or even the database settings, but by the server configuration. It is the web server, in our case Nginx, that sets the bandwidth limit for the project, as it determines the routing of requests, response speed, caching efficiency, and resistance to high loads.
If a site receives at least a few thousand visits per day, any unfinished Nginx directives begin to manifest themselves in the form of errors, delays, and other unpredictable behavior. A properly configured server, on the other hand, allows WordPress to run stably, quickly, and with a large performance margin. In this article, we will look at a complete set of configurations that turn a regular VPS server into a ready-made platform for a serious WordPress project.
PHP-FPM is the core of dynamic request processing in WordPress. Its configuration determines how fast the site's backend will work and what load will be placed on the CPU and RAM. Errors in the PHP-FPM configuration often look like “site slowdowns,” even though the PHP logic itself may be working perfectly.
The main problem is the use of default pool parameters. For example, the configuration pm = dynamic with too low a value of pm.max_children creates an effect where the server looks “half-empty” while WordPress serves a queue of requests. Correct process configuration allows you to distribute the load evenly and prevent freezes under peak loads.
To make it easier for you to select the parameters you need, we have provided a simple reference table below.
Table - PHP-FPM parameter selection
| RAM | pm.max_children | pm.start_servers | pm.min_spare_servers | pm.max_spare_servers |
|---|---|---|---|---|
| 1 GB | 3-4 | 1 | 1 | 2 |
| 2 GB | 6-8 | 2 | 2 | 4 |
| 4 GB | 10-15 | 3 | 3 | 6 |
If you still want to determine the exact values, then you need to monitor the processes. To do this, you can use the following command:
ps aux | grep php-fpm journalctl -u php8.2-fpm top -Hp $(pidof php-fpm)
The conclusions are quite simple: if the processes reach their maximum, increase the limits, and if the RAM runs out, decrease them.
To work with Nginx, it is better to use a Unix socket:
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
It works faster and more stable than TCP connections, especially on weak VPS. This improvement provides a noticeable performance gain and reduces delays between Nginx and PHP-FPM.
WordPress uses a front controller in its work, where almost all requests go to index.php. This allows you to flexibly manage your site's URL structure, human-readable URLs, custom post types, and REST API. But this scheme only works if the try_files directive is configured correctly.
One line is enough to either ensure stable website operation or completely break its routing. Errors in the description of try_files are the cause of a huge number of incorrect 404s, non-working plugins, and incorrect redirects. The basic correct configuration looks like this:
try_files $uri $uri/ /index.php?$args;
Inexperienced administrators often make mistakes that can lead to a variety of unpleasant consequences. In the table below, we have listed the most common ones and described the consequences of these errors.
Table - errors in the try_files configuration
| Error | Consequence |
|---|---|
Missing $args parameter |
Pagination breaks, as well as WordPress search and WooCommerce filters |
| Arguments order changed | Some pages start being served as static content |
Duplication across multiple location blocks |
Different plugins may route requests inconsistently |
Passing only /index.php |
Some URLs work without GET parameters and behave incorrectly |
To check the correctness of URL processing, you can use the following command:
curl -I https://site.com/test/ curl -I https://site.com/page/2/?filter=color
If the server returns a 404 where it shouldn't, then there is an error in try_files.
WordPress dynamically generates only the HTML part of the page. Everything else, including images, style sheets, fonts, and JavaScript, can and should be served by Nginx, without the involvement of PHP-FPM. This significantly reduces the load on your server.
Properly selected caching headers allow the browser and CDN to almost completely eliminate repeated requests for static resources. Here is a simple example of a caching configuration:
location ~* \.(jpg|jpeg|png|gif|svg|css|js|ico|woff2?)$ {
expires 30d;
add_header Cache-Control “public, no-transform”;
}
Table - Cache lifetime depending on file type
| File type | Recommended cache duration |
|---|---|
| CSS, JS | 30–90 days |
| WOFF2 | 180 days |
| PNG/JPG/SVG | 30–180 days |
| Theme files | 7–30 days (if updated frequently) |
The durations given in this table are still approximate, and we recommend that you select them yourself depending on your content management style or the predominant type of content posted on your website. Proper caching can reduce the proportion of PHP requests from 70% to 5-10% of the total number of requests received by the server.
Compression is perhaps one of the most underrated optimization tools. It significantly reduces the size of the server response packet and thus speeds up the loading of the site. Most configurations are limited to using gzip, although Brotli provides better compression in some cases and has long been supported by almost all browsers.
For websites with a lot of CSS and JS, Brotli can reduce the response weight by 20-25% more than gzip.
gzip on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript application/xml;
brotli on;
brotli_comp_level 5;
brotli_min_length 1024;
brotli_types text/plain text/css application/javascript application/json application/xml+rss application/xml application/font-woff application/font-woff2 image/svg+xml;
IMPORTANT! Brotli is not a panacea, and there are at least a few cases when it should not be used:
WordPress allows you to upload large images, PDFs, archives, and other heavy files. If Nginx settings do not take this into account, users may encounter errors and be unable to download content.
By default, Nginx only allows files up to 1 MB to be uploaded, which makes WordPress virtually unusable. Therefore, it is recommended to immediately increase this limit to 64 MB by default:
client_max_body_size 64M;
However, when working with WooCommerce, CSV imports, or video content, this limit should be increased to 100-200 MB. Also, when uploading large files, it is recommended to disable buffering, as this reduces the load on the disk during the upload of large files:
proxy_request_buffering off;
The modern internet requires the mandatory use of HTTPS. Due to the fact that WordPress is currently the undisputed leader among CMSs, it is particularly vulnerable to bots, scanners, and data interception. Therefore, the server must provide secure and correct encryption.
Incorrect HTTPS configuration can cause redirect loops, mixed content issues, and slowdowns.
Let's start with the mandatory header:
add_header Strict-Transport-Security “max-age=31536000” always;
This header enables the HTTP Strict Transport Security (HSTS) mechanism, one of the key technologies that improves the security of a website running on HTTPS.
There are also several optional but desirable headers. For example:
add_header X-Frame-Options “DENY” always;
add_header X-Content-Type-Options “nosniff” always;
add_header Referrer-Policy “strict-origin” always;
add_header X-XSS-Protection “1; mode=block” always;
add_header Permissions-Policy “geolocation=(), microphone=(), camera=()” always;
What each of them does:
| Header | Purpose |
|---|---|
| X-Frame-Options DENY | Completely prevents the site from being opened inside an iframe, providing strong protection against clickjacking |
| X-Content-Type-Options nosniff | Prevents MIME-sniffing and the loading of potentially dangerous files |
| Referrer-Policy strict-origin | Sends only the domain, not the full URL, during navigation, improving user privacy |
| X-XSS-Protection | Enables built-in XSS filtering in older browsers |
| Permissions-Policy | Explicitly blocks unnecessary APIs such as geolocation, microphone, camera, etc. |
Minimal SSL configuration is a set of settings that does not attempt to “get the maximum score in tests,” but provides an adequate balance between security, compatibility, and ease of support. For most WordPress projects, it is enough to enable modern protocols, secure ciphers, and regularly update certificates to be confident about the security of your project. Below, we will provide the basic SSL configuration in Nginx, as well as a detailed description of what each parameter does and how to incorporate it into your web server's working configuration.
So, the basic block in the server (for port 443) might look something like this:
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# The rest of the site configuration...
}
Let's take a closer look at the key parameters:
ssl_protocols TLSv1.2 TLSv1.3;This directive specifies which TLS versions are allowed to connect to the site.
If the site is targeted at very old devices, such as older Android or older built-in browsers, then you will have to evaluate the statistics and proceed accordingly, but for modern projects, the combination of 1.2 + 1.3 is a reasonable minimum.
ssl_ciphers HIGH:!aNULL:!MD5;This directive is responsible for the set of ciphers (cipher suites) that the server allows to be used.
HIGH- includes only strong ciphers;!aNULL- prohibits ciphers without authentication (anonymous key exchange), which are vulnerable and do not guarantee authenticity;!MD5 - excludes ciphers that use MD5, as MD5 is considered cryptographically insecure.In fact, this line filters the list of possible ciphers down to a set that provides normal security without exotic and weak combinations.
ssl_prefer_server_ciphers on;
By default, the client (browser) may attempt to impose a cipher from the list of supported ones on the server. Enabling this option directly tells Nginx:
As a result, it is the server that controls security, rather than relying on possible oddities on the client side.
Modern WordPress sites increasingly rely on REST API. It is used by WooCommerce online stores, integrations with external services, React/Vue SPA applications, and any headless architectures. REST API processes dynamic requests, so it is important that Nginx directs them to WordPress correctly and does not try to interpret them as regular files. If this is not done, API routes may return a 404 code, work slowly, or conflict with other routing rules.
To ensure that Nginx passes requests to the REST API to the WordPress core, a separate block is used:
location /wp-json/ {
try_files $uri /index.php?$args;
}
This construct forces all API requests to be sent to index.php, ensuring correct route processing, WooCommerce operation, and external integrations.
Now let's move on to XML-RPC.
XML-RPC is an old mechanism for remote access to WordPress that was used before the REST API appeared. Today, it is only used by a few applications (such as mobile apps on WordPress) and some older integrations. However, the xmlrpc.php file itself is one of the most frequently attacked entry points, with attackers sending thousands of requests through it to brute-force passwords.
If your project does not use XML-RPC, it is safer to disable it:
location = /xmlrpc.php { deny all; }
This reduces the load on the server, decreases the risk of brute force attacks, and makes the system more secure without affecting the functionality of most modern websites.
Even the perfect WordPress can “go down” if hundreds of requests are sent to wp-login.php or XML-RPC in a minute. Limiting the frequency of requests mitigates such attacks and prevents PHP-FPM overload. In WordPress, this is handled by so-called limits or rate limits. Here is an example of a basic limit configuration that will work for most projects:
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
limit_req zone=one burst=20 nodelay;
Table - Limit recommendations
| Site type | Recommended limit |
|---|---|
| Blog | 10–15 r/s |
| News site | 20–30 r/s |
| E-commerce | 30–50 r/s |
| Corporate website | 10 r/s |
You can check whether the set limits are working by viewing the log with the command:
grep “limiting requests” /var/log/nginx/error.log
We have already touched on the topic of caching static files above. But it is not only static files that can be cached.
FastCGI cache is one of the most powerful tools for speeding up WordPress today. It allows Nginx to serve HTML pages without calling PHP, which significantly reduces the load and increases the server's response speed.
Unlike caching plugins, FastCGI works at the web server level and is much more effective when the resource has high traffic.
A basic example of a FastCGI configuration looks something like this:
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
fastcgi_cache_key “$scheme$request_method$host$request_uri”;
fastcgi_cache_use_stale error timeout invalid_header http_500 http_503;
IMPORTANT! FastCGI cache cannot be used with all components; there are exceptions due to the logic of these components.
Even a correctly configured server can encounter configuration errors, lack of resources, or incorrect paths. The ability to quickly diagnose a problem is an important part of operating a WordPress project for its administrator or owner. Therefore, below we have provided several commands that greatly facilitate this task.
Checking the Nginx configuration:
nginx -t
Viewing errors in the log:
tail -f /var/log/nginx/error.log
Checking the server response:
curl -I https://site.com/
Simple load testing:
ab -n 1000 -c 50 https://site.com/
Most often, PHP-FPM hits a limit set by pm.max_children. In this case, requests are queued and the site slows down, even if the processor is “empty.” Increase the process limit and check the load again.
Look at the response headers:
curl -I https://site.com/
If the cache is configured correctly, you will see X-FastCGI-Cache: HIT or MISS.
If there is no header, it means that the cache is not being used or is not configured.
Usually, the path to the socket is incorrect. Check:
ls /run/php/
and compare it with the directive:
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
If the paths match, make sure PHP-FPM is running.
Yes, if your project generates pre-compressed .gz versions of files (e.g., frontend builders).
If there are no such files, the parameter can be left disabled, as it will not change anything.
The simplest and most effective way is to simply limit the frequency of requests:
limit_req zone=one burst=5 nodelay;
Or allow access only to certain IPs if the login is rarely used.
If you don't use the WordPress mobile app or older integrations, then yes, it's better to disable it:
location = /xmlrpc.php { deny all; }
This reduces the risk of brute force attacks and reduces the load on the server.
WordPress performance is not just about plugins and theme optimization. The main components of speed, such as PHP processing, correct routing, effective caching, security, and load control, are provided at the web server level.
By following the settings described in this article, you can turn an ordinary inexpensive VPS into a reliable platform capable of handling serious projects and high loads. Such a server will ensure stability, improve your project's SEO metrics, reduce resource consumption, and increase the overall responsiveness of the site. For businesses, this means faster page loading, better conversion rates, and resistance to seasonal and daily traffic spikes.
Effective backup strategies for Docker applications: how to protect volumes, data, and configurations while avoiding common mistakes, and quickly restore servic...
A clear guide to what VPS is used for: real cases, examples for developers, business setups, VPN, CI/CD and more. Learn how to choose the right VPS plan.
SOLID principles help create flexible, scalable, and maintainable code. We break down SRP, OCP, LSP, ISP, and DIP with examples and practical recommendations.