Wordpress – Ostrich blog https://ostrich.kyiv.ua Tue, 04 Nov 2025 17:06:11 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.3 https://ostrich.kyiv.ua/wp-content/uploads/2024/02/ostrich-150x150.png Wordpress – Ostrich blog https://ostrich.kyiv.ua 32 32 WordPress benchmark: Apache vs. Nginx https://ostrich.kyiv.ua/en/2025/11/04/wordpress-benchmark-apache-vs-nginx/ https://ostrich.kyiv.ua/en/2025/11/04/wordpress-benchmark-apache-vs-nginx/#respond Tue, 04 Nov 2025 17:01:24 +0000 https://ostrich.kyiv.ua/?p=1837

The performance of WordPress websites has always been a relevant topic – especially for those hosting their projects on low-power but energy-efficient devices like the Raspberry Pi 4.

Apache, historically the default web server in many Linux distributions, remains popular due to its simplicity and broad compatibility. However, in recent years Nginx has positioned itself as a faster and lighter alternative, particularly when handling a large number of concurrent connections.

In this article, I decided to compare these two web servers across several metrics such as First Contentful Paint, Largest Contentful Paint, Total Blocking Time, and Speed Index – key parameters commonly used in performance benchmarks.

Introduction

When I first deployed this blog, I used Apache as the default web server. Of course, WordPress “out of the box” is quite heavy, and over time, as I added more plugins, the site’s performance gradually declined. That made me curious to try Nginx.

Apache and Nginx work on fundamentally different principles, so a direct comparison isn’t always fair. However, using performance benchmarks provides an objective way to measure real-world results – and that’s exactly what this post explores.

Testing Methodology

To test website performance, I relied on two well-known benchmarking tools:

  • Google PageSpeed Insights
  • ApacheBench (ab)

Before migration, I ran each test three times – in the morning, afternoon, and evening – and then averaged the results. I repeated the same process under Nginx, and once again after additional Nginx optimization with FastCGI caching.

Let’s take a look at the results!

PageSpeed Insights тест

Google PageSpeed Insights is an online tool that evaluates web-page performance from the perspective of real-world users. It analyzes pages on both mobile and desktop, measuring key loading experience indicators known as Core Web Vitals:

  • Largest Contentful Paint (LCP) – time it takes to render the main visible content;
  • Total Blocking Time (TBT) – the period when the page remains unresponsive after load;
  • Cumulative Layout Shift (CLS) – visual stability during rendering;
  • plus additional metrics such as First Contentful Paint (FCP) and Speed Index.

Based on these measurements, PageSpeed Insights assigns a performance score and offers optimization recommendations. This helps assess not only server response speed but also the overall user experience.

Performance can vary depending on the device type – mobile and desktop. PageSpeed Insights displays the mobile result first, but you can switch to desktop view afterward. I averaged the data and visualized the results in comparative charts.

Mobile Results

Switching from Apache to Nginx alone gave a modest improvement – about 15 %. But after enabling FastCGI cache, the results changed dramatically:

  • the site began serving pages almost three times faster,
  • the main content (LCP) appeared in under 4 seconds,
  • and Total Blocking Time dropped by 40 %.

This improvement occurs because mobile devices have weaker CPUs and slower connections. Any form of server-side caching (Nginx FastCGI + Cloudflare Edge) has a significant effect: the browser no longer waits for PHP execution on the Raspberry Pi – it receives ready-made HTML instead.

Caching in Nginx turned out to be the most impactful optimization for WordPress on Raspberry Pi 4. The site moved from the “slow” category into a stable medium-to-fast range, and mobile visitors now see content roughly three times sooner than with Apache.

Desktop Results

On powerful desktop devices, the difference between Apache and Nginx is smaller, since browsers and network stacks process data faster than the Raspberry Pi can generate it. Here we can see:

  • FCP and LCP are nearly identical – the site already loads quickly;
  • TBT with caching drops to 15 ms, meaning the page renders instantly;
  • Speed Index in the cached version (1.3 s) is slightly worse than in the original (1.1 s).

Why? Speed Index measures how quickly the page becomes visually complete, not just the server response. In the cached setup, the browser receives content faster, but Cloudflare/Nginx may deliver it with slightly different headers (cache-control, content-encoding: zstd), which can alter the load order of small assets (CSS, JS, fonts). For a desktop user, this difference – a few tenths of a second – is statistically negligible and imperceptible in practice.

ApacheBench

ApacheBench (ab) is a command-line utility for measuring web-server performance. It simulates load by sending many concurrent requests and measures how quickly the server responds.

The test command:

ab -n 200 -c 50 https://ostrich.kyiv.ua/en/
  • -n 200 – total number of requests (200 pages in a row)
  • -c 50 – number of concurrent users (simulating 50 simultaneous visitors)

This test shows how well the Raspberry Pi 4 handles real load under Apache, Nginx, and Nginx with FastCGI cache.

Without caching, the difference between Apache and Nginx was minimal – both handled around 108–109 requests per second. Nginx was slightly faster at establishing connections (lower “Connect” time), but spent a bit more time processing PHP responses, resulting in roughly equal total throughput.

Enabling FastCGI cache, however, changed everything:

  • throughput increased from 109 to 141 requests per second,
  • average response time decreased by over 100 ms,

Each WordPress page is now served directly from Nginx cache, without invoking PHP, allowing the system to handle multiple simultaneous users quickly and consistently.

In short, the Nginx + FastCGI Cache combination turns a Raspberry Pi 4 into a lightweight yet production-grade web server, capable of sustaining WordPress workloads with dynamic content.

Conclusions

Switching from Apache to Nginx on Raspberry Pi 4 demonstrated that even a small home server can achieve a noticeable performance boost. Nginx alone proved slightly more efficient under high concurrency, but the real advantage came after enabling FastCGI cache.

Caching offloaded PHP and the database, making WordPress pages render almost instantly. This effect was most pronounced on mobile devices, where responsiveness improved severalfold and interactions became smoother.

On desktop systems, the improvement is less visible because modern browsers and connections are already fast – yet even there, Nginx with cache ensures more stable delivery and minimal rendering delay.

Overall, the combination of Nginx + FastCGI Cache + Cloudflare CDN transforms the Raspberry Pi 4 into a fully capable production-level web server – fast, stable, and optimized for modern performance standards.

]]>
https://ostrich.kyiv.ua/en/2025/11/04/wordpress-benchmark-apache-vs-nginx/feed/ 0
Cloudflare: real experience repelling a DDoS attack from russia https://ostrich.kyiv.ua/en/2025/05/25/cloudflare-real-experience-repelling-a-ddos-attack-from-russia/ https://ostrich.kyiv.ua/en/2025/05/25/cloudflare-real-experience-repelling-a-ddos-attack-from-russia/#respond Sun, 25 May 2025 13:23:47 +0000 https://ostrich.kyiv.ua/?p=1219 Introduction

When I bought a domain and decided to deploy a public site on my Raspberry Pi (this blog you are reading now) I knew that a DDoS attack would happen, for sure, but it was only a matter of time. That time has come. In this post, I’ll share my experience of how I fended off a DDoS attack with Cloudflare on a free plan. I will provide here the graphs and results of the work on the fact of the completion of the attack, but they will be quite informative for visual perception.

Preventive measures

I continue to study modern network capabilities, new technologies, understand settings and services. I currently have several web services installed on my server, but from a security point of view, I have restricted access from the Internet to only the local network. Such measures cannot apply to a public web resource – this blog. After writing about the 5th article, I thought about protection against DDoS attacks, and the first thing that came to mind was the Cloudflare service.

Cloudflare – is a powerful platform for ensuring security, optimizing performance and increasing the stability of web resources. Thanks to a global network of data centers, Cloudflare acts as an intermediate link between the site and the visitor: it caches content, blocks unwanted traffic and provides analytics.

At the entry level, I chose the free plan, which has some limitations unlike the commercial one, but it is quite enough for basic site protection needs. To start using Cloudflare, you need to follow a few simple steps:

  • Sign up for Cloudflare
  • In the admin panel of the domain registrar it is necessary to register the DNS records of the Cloudflare servers
  • In the admin panel of Cloudflare add DNS records of your physical server

After making the changes, the reindexing of DNS records will begin, which can take up to 72 hours, but in my case it happened in less than a day. After that, I breathed a sigh of relief and started using the site fully relying on Cloudflare.

DDoS attack detection

I use Zabbix to monitor the resources of my local network. I usually monitor mail server activity, Raspberry Pi temperatures, and other metrics. Visually, I placed several widgets on the dashboard so that anomalies in the operation of services can be clearly and quickly identified

Normally the temperature is around 60 – 65°C, but I noticed an over temperature of almost 10 degrees, up to 70 – 75°C. At this time, I looked at the CPU load, and it was at the level of 50%, I immediately understood that something was wrong.

I looked at the server metrics and almost all of the graph values ​​indicated an emergency situation

Visual charts only show the big picture. Now my goal was to identify the root of the evil. To do this, I connected to the server and started viewing the status of processes and services. Unfortunately, I did not take screenshots during the diagnostics, but I copied the text from the console. I will try to reproduce the course of events based on the history of the teams.

CPU resource

With the first command, I identified the first 15 processes with high CPU consumption.

ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu | head -n 15

I got the following output:

ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu | head -n 15
    PID    PPID CMD                         %MEM %CPU
2366770 1943100 /usr/sbin/apache2 -k start   1.1 12.4
2373538 1943100 /usr/sbin/apache2 -k start   1.2 12.2
2374705 1943100 /usr/sbin/apache2 -k start   1.2 11.8
2374940 1943100 /usr/sbin/apache2 -k start   1.2 11.3
2376091 1943100 /usr/sbin/apache2 -k start   1.0 11.1
2370641 1943100 /usr/sbin/apache2 -k start   1.2 10.4
2355557 1943100 /usr/sbin/apache2 -k start   1.2 10.4
    709       1 /opt/networkoptix/mediaserv  5.7  9.6
2375784 1943100 /usr/sbin/apache2 -k start   1.0  9.3
2373539 1943100 /usr/sbin/apache2 -k start   1.2  7.8
2375416 1943100 /usr/sbin/apache2 -k start   1.2  7.1
2376564 1943100 /usr/sbin/apache2 -k start   0.8  6.9
2372672 1943100 /usr/sbin/apache2 -k start   1.2  6.9

Of course, the values ​​were 10-20% CPU per process, which in sum gave quite a large total load.

Traffic source

The second team had to determine which IPs generate a lot of traffic. This command displays the first 10 IP addresses based on the number of requests.

awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -n 10

I got the following output:

 118852 127.0.0.1
   7169 162.158.239.23
   7168 162.158.238.232
   6304 162.158.238.233
   5810 162.158.239.24
   5068 162.158.238.106
   4848 162.158.238.107
   3871 162.158.238.120
   3843 162.158.238.121
   2875 104.23.217.65

This information reflects local requests (127.0.0.1) – more than 118 thousand requests, that is, someone from the server actively requests the site. The pool of IP addresses 162.158.x.x and 104.23.x.x is owned by Cloudflare, as a proxy, so the real traffic is hidden behind Cloudflare and certain rules of my router are not working.

In addition, geo-filtering of traffic is already configured on my router, which excludes any traffic from russia and belarus.

Virtual host involvement

The next step is to determine which site or virtual host is loading the server. To do this, we will analyze the first 100 lines of the log, however, for improved statistics, this number can be increased. Since I detected this attack at an early stage, 100 lines is enough.

sudo tail -n 100 /var/log/apache2/access.log | awk '{print $7}' | sort | uniq -c | sort -nr | head -20

I got the following output:

29 /wp-login.php
22 /wp-admin/images/
7 /zabbix/zabbix.php?action=widget.item.view
4 /zabbix/zabbix.php?action=widget.svggraph.view
1 /wp-cron.php?doing_wp_cron=1748089832.8370869159698486328125
...

If you ignore requests to Zabbix, then the main requests go to WordPress. To summarize, the result of the diagnosis is as follows:

  • apache2 – consumes more than all the resources of the processor
  • local IP and Cloudflare IP – has the most requests
  • requests to WordPress pages

At the first stage, these diagnostic data turned out to be sufficient to determine further actions.

Cloudflare Security Actions

I didn’t expect Cloudflare to be such a powerful tool, even in the free version. Many features are disabled by default and must be enabled manually for certain attacks, and it is recommended to deactivate these settings after the attack is over. The main mode is I’m under attack mode.

I’m under attack mode

I’m Under Attack Mode is a special Cloudflare feature designed for protection of websites from DDoS attacks, in particular at the level HTTP flood (e.g., bulk GET/POST attacks). The basic principle of operation of this mode is as follows:

  • Before each visit to the site an intermediate verification page is displayed, which lasts a few seconds.
  • Cloudflare does JavaScript challenge — that is, forces the browser to “prove” that it is not a bot.
  • If the check is successful, the user gets to the site.
  • Bots and simple scripts that cannot execute JS – automatically are sifted out.

This mode can be enabled in the Security -> Settings section, there is a switch I’m under attack mode, it must be activated, and the work of repelling the attack will begin immediately.

In the new interface, this item was moved to the All settings tab at the bottom, and named Security Level. When you click on Edit, you can enable or disable I’m under attack mode.

After activating this mode, a message will appear on the dashboard:

Under Attack Mode is active
Under Attack mode is used when a website is under a DDoS attack. All visitors will be shown an interstitial page for a short duration.

In this mode, there is a small delay with the check, which looks like this:

In the Security -> Events section, you can see records about the initiator of the requests, rather complete and organized information is displayed. In the new interface, the Events section has been moved to the Analytics menu and the Events tab has been created, where the information is structured in the same way.

Custom and IP access rules

Given that the DDoS attack occurred from a single IP address, rules can be applied to it.

  • IP access rules – to block IP addresses individually or using a subnet mask
  • Custom rules – more flexible setting of rules

First, I decided to create a rule that would block the attacking server by IP address, for this in the new interface you need to go to the Security -> Security rules -> IP access rules section and click on the +Create button. To create a rule, it is enough to fill in only 4 fields:

  • IP, IP range, country name, or ASN – 194.67.196.50
  • Action – Block
  • Zone – This website
  • Notes – Fucking russian hacker

From the very beginning, I wanted to block all of russia by geolocation, but in the free tariff plan, at the level it is not possible to do so, after applying this rule I see an error: Sorry, block by country is only available on the Enterprise plan.

It turns out that there is a geolocation filter in the free version, but it can be configured in the Custom rules section.

  • Rule name (required) – Block_country_404
  • Field – Country
  • Operator – equals
  • Value – russian federation
  • Then take action – Block
  • Place at – First

This way the rule will look like this

Reports

In the Security -> Overview section, you can see a graph of the ratio of restricted, Cloudflare-handled and server-handled traffic to the total number of requests. When Cloudflare is active, the value of the traffic limit (Mitigated) will increase, and the number of cached requests will also increase, because Cloudflare in this mode works without direct traffic and has a number of restrictions to prevent hacking, such as password selection by brute force.

Many useful graphs are displayed on the dashboard – Overview. I took several screenshots, at the beginning of the attack and at the end of it. The graph shows the last 24 hours of activity, so I’ve overlayed these two graphs to show you the big picture of activity. I marked the range of Cloudflare’s service from start to finish with a red rectangle.

The graphs are quite informative and setting up Cloudflare is easy. According to the schedule, the attack lasted 17 hours from the beginning, and thanks to my vigilance, at the beginning of the attack, at the 6th hour, I applied Cloudflare protection, and already in 11 hours the attack was stopped, I think because of its irrationality for the attacker.

After the attack was over, I turned off the I’m under attack mode and continue to monitor server activity to be ready to prevent repeated attacks at any time!

Conclusions

The experience of fighting a DDoS attack on my own server has shown that even with minimal resources – a Raspberry Pi, my own blog, and a free Cloudflare plan – you can effectively protect yourself from threats if you have a basic understanding of how the network, servers, and monitoring systems work.

I was convinced that:

  • Preventive actions are important. Installing Cloudflare in advance allowed me to quickly enable protective mechanisms.
  • Monitoring tools such as Zabbix help to quickly detect anomalies and localize the source of the problem.
  • Cloudflare has proven to be extremely effective even in its free version, in particular thanks to the I’m Under Attack mode, which allows you to filter out malicious traffic before it even reaches the server.
  • Openness to learning and log analysis is the key to a quick response. The ps, awk commands, and access.log analysis helped not only identify the source of the load, but also draw conclusions about the type of attack.

This case confirmed that reliable protection is not always expensive infrastructure, but primarily attentiveness, readiness to respond, and the ability to work with available tools.

My blog remained online, and this is the best indicator of the effectiveness of the solutions applied.

]]>
https://ostrich.kyiv.ua/en/2025/05/25/cloudflare-real-experience-repelling-a-ddos-attack-from-russia/feed/ 0
How to Make Full-Width Content in the WordPress https://ostrich.kyiv.ua/en/2025/04/25/how-to-make-full-width-content-in-the-wordpress/ https://ostrich.kyiv.ua/en/2025/04/25/how-to-make-full-width-content-in-the-wordpress/#respond Fri, 25 Apr 2025 13:29:56 +0000 https://ostrich.kyiv.ua/?p=1008 Introduction

The Twenty Twenty-Two theme, part of WordPress’s default theme lineup, is built using the modern Full Site Editing (FSE) approach. While this brings a lot of flexibility, it also introduces some layout limitations that may not be immediately obvious. One such limitation is that content is not full-width by default, even when you want it to be.

In this article, I’ll show you a simple fix using the theme.json file to allow your content to span the entire width of the screen.

Why Does This Matter?

In many design scenarios, you want your content to stretch from edge to edge, especially when using blocks like Cover, Gallery, or full-width Images. Without this adjustment, your site can look cramped and constrained.

One of the most noticeable issues with the default layout settings is how they behave on mobile devices. The built-in padding adds unnecessary margins around your content, making it appear squeezed and significantly reducing the usable screen space. This affects the readability and overall aesthetic, especially on phones with smaller screens. If you want your site to look clean and professional on mobile – adjusting the layout is essential.

There are screenshots with 85% of Layout: Inner blocks use content width Nested blocks use content width with options for full and wide widths. In my case:

  • Content width = 85%
  • Wide width = 85%

Update Layout Settings

Use the Site Editor (Visual Method) do next steps:

  • In the top left, click Document Overview button
  • Select the root block. In my case it is Query Loop block
  • In the top right, click style button
  • Click Layout link
  • Set the content width and wide width to 100%
  • Click Settings button, turn Inner blocks use content width switcher ON and set 100% for content width and wide width too

You can see difference in the width, but only inner block content width

This approach is perfect if you’re using a block theme like Twenty Twenty-Two and want a no-code solution to control your layout.

Adjust Outer Spacing

Edit theme.json via the Theme File Editor (No FTP Needed) If you’re not using an external code editor or FTP access, WordPress provides a built-in tool:

  • Navigate to Tools → Theme File Editor.
  • On the right-hand side, find and click on theme.json.

Find code block:

"custom": {
	"spacing": {
		"small": "max(1.25rem, 5vw)",
		"medium": "clamp(2rem, 8vw, calc(4 * var(--wp--style--block-gap)))",
		"large": "clamp(4rem, 10vw, 8rem)",
		"outer": "var(--wp--custom--spacing--small, 1.25rem)"
	},

In this case we see two main rows for next variables which need be edited:

--wp--custom--spacing--outer
--wp--custom--spacing--small

Cnahge rem and vw values to zero (0) and click update file.

"custom": {
	"spacing": {
		"small": "max(0rem, 0vw)",
		"medium": "clamp(2rem, 8vw, calc(4 * var(--wp--style--block-gap)))",
		"large": "clamp(4rem, 10vw, 8rem)",
		"outer": "var(--wp--custom--spacing--small, 0rem)"
	},

Large and Medium values still old settings and not modify.

You can see message: File edited successfully, reload the page, if needed, clear cache.

In this case you need set up internal spaces using Padding and Margin values of your blocks. I managed it and now my site fit 100% width of my phone:

Note: If you’re using a caching plugin like W3 Total Cache or similar, you may need to clear the cache after making changes. Otherwise, updates – especially to layout or text content – might not appear immediately on the front end of your site.

If everything worked correctly, your content should now span the full width of the screen – 100% – on both mobile and desktop devices. While this looks clean and modern on mobile, it can appear too wide on large desktop screens. For a more polished layout, it’s recommended to adjust the layout width from 100% to a fixed maximum value, such as 1200px. This way, your site will have a centered and compact appearance on desktop while still retaining an edge-to-edge look on mobile, without side padding or gaps.

Final Layout Recommended Settings

  • Layout: Set both Content width and Wide width to 1200 pixels.
  • The root Query Loop block should have the layout switch enabled (in the Layout section), but leave the specific width values empty – this will automatically inherit the global layout width (1200px in this case).
  • In this example, all other inner blocks should have the layout switch turned off, so they follow the parent block’s layout.

Additional margin and padding values can be customized based on your personal design preferences.

Conclusion

With just two simple modifications in your theme.json file, you can easily remove width restrictions in the Twenty Twenty-Two theme and make your content full-width, significantly improving the visual experience of your site. This is especially useful for landing pages, portfolios, or any visually rich layout.

If you’re working with FSE, don’t be afraid to edit theme.json – it’s a powerful tool that gives you control over many aspects of your theme without the need for CSS hacks.

]]>
https://ostrich.kyiv.ua/en/2025/04/25/how-to-make-full-width-content-in-the-wordpress/feed/ 0
Creating a Custom Field for the Polylang Plugin in WordPress https://ostrich.kyiv.ua/en/2025/04/22/creating-a-custom-field-for-the-polylang-plugin-in-wordpress/ https://ostrich.kyiv.ua/en/2025/04/22/creating-a-custom-field-for-the-polylang-plugin-in-wordpress/#respond Tue, 22 Apr 2025 12:17:45 +0000 https://ostrich.kyiv.ua/?p=985 Introduction

In many WordPress projects, there is a need to translate not only standard interface elements like titles, menus, or post content but also custom strings located, for example, in the header or footer. Polylang is one of the most popular plugins for implementing multilingual functionality. However, it does not natively support translating custom strings directly embedded into templates. In this article, we’ll explore how to manually register such strings using Polylang.

Polylang is a WordPress plugin that enables multilingual websites. It allows users to translate posts, pages, media, categories, tags, and more. Key features:

  • Simple interface for adding translations.
  • Support for multiple languages.
  • Integration with popular themes and plugins.
  • Language switcher functionality.

Limitations:

  • Custom strings in templates are not translated by default.
  • Manual registration of such strings is required.

Registering Custom Translation Strings

To register multiple strings you need to edit functions.php file. I use theme twentytwentytwo so file located there: /var/www/html/wp-content/themes/twentytwentytwo/functions.php

I connected to my server and edited file using nano editor:

sudo nano /var/www/html/wp-content/themes/twentytwentytwo/functions.php

I created a list of fields to make my further fork easier. I put php function code on the end of file:

// Add Polylang translate function
function register_custom_polylang_strings() {
    $strings = [
        'Custom 1:'     => 'Custom Labels',
        'Custom 2:'     => 'Custom Labels',
        'Custom 3:'     => 'Custom Labels',
        'Custom 4:'     => 'Custom Labels',
        'Custom 5:'     => 'Custom Labels'
    ];

    foreach ( $strings as $string => $group ) {
        pll_register_string( sanitize_title($string), $string, $group );
    }
}
add_action('init', 'register_custom_polylang_strings');

In future you can change these fields to make identification of translation. It is just sample. After saving file and reloading page you can see these fields in the translations list.

Creating a Translation Shortcode

Easy way to get translation data is a creating a translation shortcode. Add a shortcode to the same functions.php to display registered strings

function pll_translate_shortcode($atts) {
    $atts = shortcode_atts([
        'text' => '',
    ], $atts);
    return pll__($atts['text']);
}
add_shortcode('tr', 'pll_translate_shortcode');

You need to save this file and reload WordPress admin page to see changes.

In this case we will use shortcode like this:

[tr text="Custom 1:"]

Caution: If you change name of field in the function, you need to change it in wordpress, where you use it.

Adding Translation

So we can see custom fields and we need fill it with two languages:

  • Go to Languages → Translations in the admin panel.
  • Locate the group Custom Labels.
  • Find the string (e.g., custom-1:).
  • Add translations:
    • In Ukrainian: Перемикач мов:
    • In English: Language switcher
  • Save changes.

Using shortcode in a Template

You can now use the shortcode in any template location, such as the footer. Select shortcode block and add the code:

[tr text="Custom 1:"]

Save template and reload page to see result. It is perfect.

Result looks like for Ukrainian and English languages. Next you can use custom style to change design of this block.

Conclusion

Although Polylang lacks out-of-the-box support for translating custom strings, this limitation is easily overcome with manual registration via functions.php and shortcode implementation. This method enables full multilingual support even in complex or custom WordPress templates.

]]>
https://ostrich.kyiv.ua/en/2025/04/22/creating-a-custom-field-for-the-polylang-plugin-in-wordpress/feed/ 0
How to Move a WordPress Site to a New Raspberry Pi using rsync backup https://ostrich.kyiv.ua/en/2025/04/11/how-to-move-a-wordpress-site-to-a-new-raspberry-pi-using-rsync-backup/ https://ostrich.kyiv.ua/en/2025/04/11/how-to-move-a-wordpress-site-to-a-new-raspberry-pi-using-rsync-backup/#respond Fri, 11 Apr 2025 10:13:59 +0000 https://ostrich.kyiv.ua/?p=862 Introduction

Migrating a WordPress website to a new server is no easy task – especially when minimizing downtime is crucial. In this article, I’ll show you how I successfully moved my WordPress site from one Raspberry Pi to another, keeping downtime to just a few minutes. This step-by-step guide covers everything from setting up LAMP and Certbot to backing up files and replacing SD cards. Whether you’re migrating between Raspberry Pis or different hosting environments, this tutorial has you covered.

After a recent system failure, many services on my main Raspberry Pi didn’t start properly. However, Apache and the database remained functional, keeping my WordPress site alive. Rather than trying to fix everything on the spot, I decided to prepare a second Raspberry Pi and restore the site there from backup which was created with rsync application and stored on my PC. Once everything was ready, I just swapped SD cards to complete the transition with minimum downtime.

Prepare the New Raspberry Pi

Install 64-bit Raspberry Pi OS using Raspberry Pi Imager and enable SSH. Then update the system

sudo apt-get update
sudo apt-get upgrade

During the update, the user will encounter a hang in the update process due to the presence of a vnc server. To continue the update without interference, you must remove the vnc server:

sudo apt-get remove realvnc-vnc-server

Then continue the update. This is a feature of new Linux core which not support VNC feature.

Installing the LAMP Stack

After updating the system, you need to install the Apache, PHP, and MariaDB

sudo apt install apache2 php libapache2-mod-php php-mysql mariadb-server

Also, WordPress sometimes requires additional modules

sudo apt install php-curl php-gd php-mbstring php-xml php-zip

Check Apache service

sudo systemctl status apache2
● apache2.service - The Apache HTTP Server
     Loaded: loaded (/lib/systemd/system/apache2.service; enabled; preset: enabled)
     Active: active (running) since Wed 2025-04-09 11:37:31 EEST; 55s ago
       Docs: https://httpd.apache.org/docs/2.4/
   Main PID: 10645 (apache2)
      Tasks: 6 (limit: 9559)
        CPU: 51ms
     CGroup: /system.slice/apache2.service

Check Mariadbservice:

sudo systemctl status mariadb
● mariadb.service - MariaDB 10.11.11 database server
     Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; preset: enabled)
     Active: active (running) since Wed 2025-04-09 11:37:28 EEST; 2min 6s ago
       Docs: man:mariadbd(8)
             https://mariadb.com/kb/en/library/systemd/
   Main PID: 9401 (mariadbd)
     Status: "Taking your SQL requests now..."
      Tasks: 9 (limit: 63092)
        CPU: 411ms
     CGroup: /system.slice/mariadb.service
             └─9401 /usr/sbin/mariadbd

Since the services are working, it means everything is done correctly.

Install Certbot and Transfer Certificates

Installation of Certbot is easy, no needd to run it to generate new certificate.

sudo apt install certbot python3-certbot-apache

I already have a previously generated certificate, so I need to copy it to the new Raspberry Pi. Using the rsync command over SSH, I copy the entire contents of the /etc/letsencrypt/ directory from the PC backup to new Raspberry Pi.

sudo rsync -avz RPIB/etc/letsencrypt/ [email protected]:/etc/letsencrypt/

In this case, RPIB folder is a backup / restore folder of my main Raspberry Pi.

Backing Up WordPress data

There are two steps to do it:

  • Copy all physical files
  • Dump the database

Copy all physical files

In the time interval between the file backup and the changes to the database on the site, there were no obvious changes in the structure or new messages, so I will use the physical files from the backup stored in the RPIB directory on the computer.

sudo rsync -avz /home/home/RPIB/var/www/html/ --rsync-path="sudo rsync" [email protected]:/var/www/html/

Dump the database

On my Host Raspberry Pi I make database dump:

mysqldump -u username -ppassword wordpress_db > backup-9-4-2025.sql

And then I move it to the new RPI using rsync command

sudo rsync -avz backup-9-4-2025.sql [email protected]:/home/rpi/wrdprs-sql/

This is due to security measures that make it impossible to connect to the database remotely, even on a local network, only on the host itself. Thus, our WordPress settings and data are copied in full.

Database Settings

Most of the settings are saved in configuration files and will not be changed, so in some cases it is enough to simply copy these files. In the case of a database, it is better to create a new user, rather than using root access. First you need to connect to the database.

sudo mysql -u root -p

The root user has not been created with a password, so when prompted to enter a password, just press enter to enter the database. The next step is to create a database and a user. Give the user privileges to work with this database. Apply the changes. I will provide a template command where you need to substitute your values. This creates a database and a user with the necessary access rights.

CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

The next step is to restore the database into this database:

mysql -u wp_user -p wordpress_db < backup-9-4-2025.sql

No further database settings are required. Let’s move on to configuring the web server.

Apache Configuration

After installing the server, you need to configure a virtual host. To do this, you need to create a configuration file, or modify an existing one. I checked that I have the following default files that were previously created. I will review their contents and copy them into the new configuration:

  • 000-default.conf
  • 000-default-le-ssl.conf

To optimize the process, I copied the entire directory from PC backup to new Raspberry Pi:

sudo rsync -avz /home/home/RPIB/etc/apache2/ --rsync-path="sudo rsync" [email protected]:/etc/apache2/

However, it is too early to restart the server, because we have not prepared the certificate files. If Apache server be rebooted now, user will get the error.

Certificate Settings

Certificates should be stored by default in the /etc/letsencrypt/ directory, so you need to create these directories or move them from the backup directory

sudo rsync -avz /home/home/RPIB/etc/letsencrypt/ --rsync-path="sudo rsync" [email protected]:/etc/letsencrypt/

After that, you can check if the certbot accepts the certificate using the command:

sudo certbot certificates

You will get an approximate answer:

Certificate Name: ostrich.kyiv.ua
Serial Number: 60f74de1ba240148er7c1efa7e7cf846417
Key Type: ECDSA
Domains: ostrich.kyiv.ua
Expiry Date: 2025-06-22 08:12:46+00:00 (VALID: 73 days)
Certificate Path: /etc/letsencrypt/live/ostrich.kyiv.ua/fullchain.pem
Private Key Path: /etc/letsencrypt/live/ostrich.kyiv.ua/privkey.pem

And after that you can restart the Apache server, in this case there will be no error about the lack of certificates:

sudo systemctl restart apache2

You also need to delete the index.html file belonging to the Apache server from the /var/www/html/ directory so that the PHP index is processed when starting the site

Swap memory cards

Since the memory cards have the same settings, you can safely swap them. After rebooting the Raspberry Pi, I visited my site again, made sure that everything works. In this format, downtime was minimal!

Conclusion

This method of WordPress migration to a new Raspberry Pi shows how important it is to:

  • Have a solid backup strategy
  • Prepare the environment beforehand
  • Use the right data transfer approach

By doing everything in advance, you can achieve a minimal-downtime WordPress migration. Whether you’re upgrading your Raspberry Pi or just switching hosting hardware, this guide will help you do it with ease.

]]>
https://ostrich.kyiv.ua/en/2025/04/11/how-to-move-a-wordpress-site-to-a-new-raspberry-pi-using-rsync-backup/feed/ 0