I prefer the VPN way. It saves a lot of CPU/RAM resources since your WP will not execute checking for valid IP connection which causes some I/O, RAM, CPU resources.
Using the VPN limit IP approach you to save some resources. Once Apache found that the connecting IP is not allowed. It will drop the connection thus saving you some CPU cycles (checking MySQL) and some RAM which can be used to serve other visitors.
I use ApisCP security
I keep Fortification on Max for the Wordpress install, meaning they canāt install shit. Althought they could add code to the pages
Enabling MAX mode is like UAC in Windows, itās an added layer of authentication to restrict file uploads thatās enforced by traditional DAC permissions in the OS. Splitting permissions between the account owner and WP user makes it easier to establish audit trails, i.e. if a file is uploaded within WP directly without using FTP/SFTP, itās tagged with a different UID than if it gets uploaded using FTP (Fortification mode on).
Thereās a feature in the panel aptly called āAuditā that generates a list of such files or directories that have lax permissions.
On that topic too applying rules to deny PHP execution of /wp-content/uploads, /temp, and other media locations goes a long way in stymying drive-by hackers. Something as simple as this would work assuming mod_php,
In addition having some form of brute-force protection in front of /wp-login.php or any high-value URI (/xmlrpc.php also) is good insurance as those are typically abused. I run protection with a lower threshold against these URIs.
Most mod_security rules are overkill and getting applied non-deterministically to all URIs only hurts performance. One nice feature is the ability to pass all file creation to a third-party binary like ClamAV to determine if itās clean. You can proactively block malware before it makes its way onto the server assuming another tool like fail2ban bans these IPs before they can do further harm.
Set reasonable worker limits and monitor outages through something like Monit. Limits are intended to prevent resource monopolization and alert you of extremes. Having 20 PHP-FPM pool processes on a site that normally uses 1-2 concurrently makes it too easy to mask problems.
WordPress plugins like WordFence that rely specifically on WordPress to work happen too late in the processing axis to be viable. If itās all that you have, then itās better than nothing, but in most single-user setups compromising one file elsewhere would allow an attacker to compromise WordFence. The only thing worse than being insecure is having a false sense of security. Thereās performance concerns with it beyond this scope as well.
Above all the most important pieces of the puzzle:
Always keep your plugins and themes up to date. If a plugin or theme doesnāt work with PHP 7.4 but does with 7.3, then itās time to junk it as this hints at deeper problems in craftsmanship of that product. You wouldnāt buy a house from a carpenter that only leaks when itās raining.