Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

How to Secure a WordPress Site on a VPS

By Kokil Thapa | Last reviewed: September 2026

You moved WordPress off shared hosting onto a VPS because you needed control, speed, or predictable cost. That control cuts both ways: nobody patches the OS, firewall, or PHP stack for you anymore. Learning how to secure a WordPress site on a VPS means hardening the server first, then locking down WordPress itself. On production WooCommerce stores and client sites I maintain, the same layered approach stops most attacks before they reach wp-login.php. This guide walks through that stack on Ubuntu with Apache or Nginx, PHP 8.3+, and WordPress 7.1—copy the commands, adapt the paths, and treat security as ongoing ops, not a one-time checkbox. If you are still planning the move, read our WordPress migration from managed to VPS hosting guide first.

How do you secure a WordPress site on a VPS at the server level?

Server hardening is the foundation. WordPress plugins cannot compensate for an open SSH port, weak root password, or outdated PHP binary. Start with a fresh Ubuntu 22.04 or 24.04 image from a reputable provider. Create a non-root sudo user immediately and disable password-based root login.

Create a dedicated deploy user and lock down SSH

Never run WordPress as root. Create a user, add your SSH public key, and turn off password authentication:

# As root on a fresh VPS
adduser deploy
usermod -aG sudo deploy
mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
nano /home/deploy/.ssh/authorized_keys   # paste your public key
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh

# Harden sshd
sudo nano /etc/ssh/sshd_config
# Set: PermitRootLogin no
# Set: PasswordAuthentication no
# Set: PubkeyAuthentication yes
sudo systemctl restart sshd

Change the default SSH port only if you understand the trade-off. It reduces noise in logs but is not real security. Pair key-only auth with fail2ban configuration for PHP sites instead.

Configure UFW and allow only required ports

UFW is simple and sufficient for most WordPress VPS setups. Allow SSH, HTTP, and HTTPS. Deny everything else.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'    # or 'Apache Full'
sudo ufw enable
sudo ufw status verbose

If you use a control panel or remote MySQL, open those ports only from trusted IP ranges. Never expose MySQL port 3306 to the public internet on a WordPress VPS.

WordPress VPS Security LayersLayer 1: NetworkUFW · fail2ban · Cloudflare WAFLayer 2: ServerSSH keys · TLS · PHP-FPM poolLayer 3: WordPress2FA · updates · DISALLOW_FILE_EDITLayer 4: DataEncrypted backups · least-privilege DB userAttackers must breach every layer — not just wp-admin
Four-layer model for how to secure a WordPress site on a VPS from network edge to database

For deeper server work, our Linux system administration service covers the same stack for client VPS instances in Nepal and abroad.

Which web server and PHP settings secure a WordPress site on a VPS?

WordPress 7.1 runs well on PHP 8.3 or 8.4. PHP 8.5 is available on newer Ubuntu repos if your stack supports it. Match your web server choice to traffic and ops comfort. Both Nginx and Apache work; pick one and harden it consistently.

CriteriaNginx + PHP-FPMApache + mod_php / PHP-FPM
Memory at idleLower — good on 1–2 GB VPSHigher — needs headroom
.htaccess supportRequires manual rewrite rulesNative — plugins expect it
Security headersClean per-server blockmod_headers or vhost config
High trafficStrong static file performanceFine with caching layer
Verdict for new VPSPreferred if you know NginxFaster setup for .htaccess-heavy sites

See our WordPress Nginx vs Apache comparison for rewrite examples. Regardless of server, run a dedicated PHP-FPM pool per site on multi-site VPS hosts.

Install TLS with Let's Encrypt

HTTPS is non-negotiable in 2026. Browsers flag mixed content. Google uses HTTPS as a baseline signal. Certbot automates free certificates:

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run

Certbot adds a cron job for renewal. Verify it after every OS upgrade. Official docs live at Certbot installation instructions.

Harden PHP-FPM and disable dangerous functions

In /etc/php/8.3/fpm/php.ini (adjust version path), set sensible production values:

expose_php = Off
display_errors = Off
log_errors = On
allow_url_fopen = Off
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
upload_max_filesize = 64M
post_max_size = 64M

Some backup and image plugins need specific functions. Test after disabling. Never run display_errors = On in production—it leaks paths attackers love.

How do you harden WordPress files and permissions on a VPS?

Wrong ownership is a top cause of hacks and update failures on VPS WordPress installs. The web user needs read access to core files. It should not own everything with 777 permissions.

Set correct ownership and directory permissions

On Ubuntu with Nginx, the web user is usually www-data:

cd /var/www/example.com
sudo chown -R deploy:www-data .
sudo find . -type d -exec chmod 750 {} \;
sudo find . -type f -exec chmod 640 {} \;
sudo chmod 640 wp-config.php

WordPress needs write access to wp-content/uploads, cache dirs, and sometimes wp-content/upgrade. Grant group write only where required:

sudo chmod -R 770 wp-content/uploads
sudo chmod -R 770 wp-content/cache

Lock down wp-config.php and disable file editing

Add these constants before the "That's all, stop editing" line in wp-config.php:

define('DISALLOW_FILE_EDIT', true);
define('FORCE_SSL_ADMIN', true);
define('WP_AUTO_UPDATE_CORE', 'minor');

DISALLOW_FILE_EDIT removes the theme and plugin editor from wp-admin. Attackers who steal an admin session cannot inject PHP through the dashboard. For a full checklist, cross-reference our WordPress security hardening checklist for 2026.

Move wp-config.php above the web root when possible

If your vhost document root is /var/www/example.com/public, place WordPress core inside public and move wp-config.php one level up. Update the require path accordingly. The file stays outside direct URL access even if rewrite rules fail.

WordPress VPS Directory Layout/var/www/example.com/wp-config.php640 · outside publicpublic/ (web root)wp-admin · wp-includeswp-content/uploads 770index.php · .htaccessBlocked from webDirect URL to wp-configPHP in uploads folderDirectory listingxmlrpc.php abusedeny
Secure WordPress VPS file layout: config outside the web root with tight permissions on uploads

Block PHP execution inside uploads via Nginx or Apache rules. A uploaded "image" named shell.php.jpg is a classic attack vector on unsecured VPS WordPress sites.

What WordPress login and plugin steps secure your site on a VPS?

Brute-force attacks against wp-login.php and xmlrpc.php hit every public WordPress install. On a VPS you see them in access logs within hours of launch. Layer application controls on top of fail2ban.

Enforce two-factor authentication and strong credentials

Install a reputable 2FA plugin and require it for all administrator accounts. Pair with our WordPress two-factor authentication setup guide. Generate unique 20+ character passwords with the secure password generator—never reuse hosting panel passwords as WordPress credentials.

Limit login attempts and rename or restrict wp-login

A rate-limiting plugin or fail2ban jail on 404/403 patterns cuts automated login noise. Our WordPress login brute-force protection article covers Nginx limit_req and plugin options. Consider disabling xmlrpc.php entirely if you do not use the mobile app or Jetpack remote features:

# Nginx server block snippet
location = /xmlrpc.php {
    deny all;
    return 403;
}

Keep core, themes, and plugins updated on a schedule

Enable automatic minor core updates via wp-config.php. Schedule weekly manual checks for major releases, themes, and plugins. On a VPS you can automate with WP-CLI:

wp core update --path=/var/www/example.com/public
wp plugin update --all --path=/var/www/example.com/public
wp theme update --all --path=/var/www/example.com/public

Test updates on staging first for WooCommerce stores. We ship and maintain WooCommerce sites like Sagun Blossom Flower where a plugin conflict during checkout costs real revenue.

Attack Flow vs WordPress VPS DefencesBotnetwp-login probeUFW + fail2banIP banned at 5 failsRate limit429 response2FA gateLogin blockedIf attacker reaches wp-admin anywayDISALLOW_FILE_EDIT blocks theme injectionOff-site backup enables clean restoreFile integrity scan detects changed core filesNo single plugin replaces this stack
How layered defences stop common WordPress VPS brute-force and post-compromise damage

Use a minimal plugin set. Every installed plugin is attack surface. Delete inactive themes and plugins rather than leaving them dormant. The official WordPress hardening guide aligns with these VPS practices.

How do backups and monitoring keep a WordPress site secure on a VPS?

Backups are your last line of defence. Ransomware, a zero-day plugin flaw, or a bad deploy can compromise even a hardened VPS WordPress site. Assume breach and plan recovery before you need it.

Schedule automated off-site backups

Local snapshots on the VPS die with the server. Push encrypted backups to S3, Backblaze B2, or another region. Automate with WP-CLI and cron:

# /etc/cron.d/wp-backup — run as deploy user
0 3 * * * deploy wp db export /backups/db-$(date +\%Y\%m\%d).sql --path=/var/www/example.com/public
15 3 * * * deploy tar -czf /backups/files-$(date +\%Y\%m\%d).tar.gz -C /var/www/example.com public/wp-content
30 3 * * * deploy aws s3 sync /backups/ s3://your-bucket/wp-backups/ --delete

Read our guides on WordPress automated backups with WP-CLI and automating off-site backups to S3. Test a full restore quarterly—not a diff check, a real rebuild on staging.

Monitor logs, uptime, and file integrity

Check these weekly on every VPS WordPress install:

  1. Auth logs: /var/log/auth.log for unexpected SSH successes.
  2. Web server error log: spikes in 500 errors or PHP fatals.
  3. WordPress admin users: no unknown administrator accounts.
  4. Disk space: full disks break MySQL and corrupt backups.
  5. SSL expiry: Certbot renewal failures show up in syslog.

Uptime monitoring from an external service catches downtime your server cannot report itself. File integrity plugins or wp core verify-checksums detect tampered core files early. If you find malware, follow our WordPress malware removal step-by-step process before returning the site to production.

Harden the MySQL database user

WordPress needs one database and one dedicated user—not root. Grant only required privileges:

CREATE DATABASE wp_example CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_example'@'localhost' IDENTIFIED BY 'long-random-password-here';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX ON wp_example.* TO 'wp_example'@'localhost';
FLUSH PRIVILEGES;

MySQL 8.4 LTS or MySQL 9.7 both work. Use utf8mb4 for full Unicode including Nepali content. Store credentials only in wp-config.php, never in version control.

Secure WordPress VPS Backup CycleLive VPSWordPress 7.1Nightly exportDB + wp-contentEncrypt + S3Off-site copyQuarterly restore testStaging VPS rebuildRecovery targetsRPO: 24 hours (daily backup)RTO: 2–4 hours (documented runbook)Keep 30 days retention minimum
Backup and restore cycle every secure WordPress site on a VPS needs in production

What optional layers further secure a WordPress site on a VPS?

Once the baseline is solid, add edge protection and operational discipline. These are not substitutes for UFW, TLS, or backups—they extend them.

  • Cloudflare proxy: hides origin IP, adds WAF rules, and absorbs volumetric attacks. See WordPress Cloudflare integration.
  • Redis object cache: reduces database load under traffic spikes. Not a security tool alone, but stability prevents panic fixes that introduce holes.
  • Separate staging VPS: test plugin updates and theme changes before production.
  • Security headers: HSTS, X-Frame-Options, and Content-Security-Policy via server config.
  • ModSecurity or WAF rules: useful on Apache; Nginx equivalents exist via commercial or open-source modules.

For managed ongoing hardening, our WordPress support and maintenance service covers updates, monitoring, and incident response. New builds benefit from WordPress development with security baked in from day one.

Hosting choice matters too. Pair a secured VPS with sensible DNS and TLS management via domain registration and hosting setup. Compare broader server practices in how to secure your website and server in Nepal.

On a recent WooCommerce VPS migration for Petals Agro Nepal, we applied this exact stack before DNS cutover. The site survived a login flood on day three because fail2ban and rate limits did their job. No emergency plugin installs at midnight.

Budget roughly Rs 3,000–8,000/month (~USD 22–60) for a secured 2 GB VPS with backups and monitoring tools. Cheaper than one cleanup after a defacement incident.

Key Takeaways

  • Harden the VPS first: SSH keys, UFW, fail2ban, and TLS before hardening WordPress plugins.
  • Set file ownership to deploy:www-data with 640 files, 750 dirs, and block PHP in uploads.
  • Add DISALLOW_FILE_EDIT, enforce 2FA on all admins, and disable unused xmlrpc access.
  • Automate daily off-site backups and test a full restore to staging every quarter.
  • Use a dedicated MySQL user with least privilege—never connect WordPress as database root.
  • Treat security as weekly ops: review logs, update plugins, and verify backup integrity.

People Also Ask

Is a VPS more secure than shared hosting for WordPress?

A VPS is not automatically more secure. Shared hosts manage patches and WAF for you. On a VPS, you own the full stack. A properly hardened VPS with isolated resources is safer than overcrowded shared hosting. An neglected VPS with default passwords is far worse.

Do I need a security plugin on a VPS WordPress site?

A lightweight scanner or firewall plugin adds value for file integrity checks and login telemetry. It does not replace server-level controls. Prefer fail2ban, UFW, and proper TLS over a bloated "all-in-one security" plugin that slows every request.

How often should I update WordPress on a VPS?

Apply minor core updates automatically. Check plugins and themes weekly. Major WordPress releases should go to staging first on eCommerce or membership sites. Zero-day plugin patches may need same-day production deploys—another reason backups must work.

What is the minimum VPS spec for a secure WordPress site?

1 GB RAM works for a small brochure site with caching. WooCommerce or page-builder sites need 2 GB minimum. Security tools—fail2ban, log rotation, backup agents—consume memory too. Monitor swap usage; constant swapping is a sign to resize before performance collapses into an outage.

Build a WordPress VPS you can trust

Knowing how to secure a WordPress site on a VPS comes down to layers: network, server, application, and recovery. Skip any one layer and the rest become emergency patches instead of prevention. Start with SSH and UFW today. Schedule backups before you need them. Review logs every Friday.

Need help hardening a production WordPress VPS or migrating off shared hosting safely? Contact us for a security review, or browse the portfolio for WordPress and WooCommerce sites running this stack in production.

Frequently Asked Questions

Harden the server with UFW, fail2ban, and SSH keys; run PHP 8.3+ behind Nginx or Apache with TLS; restrict file permissions; disable file editing; enforce 2FA and strong passwords; schedule off-site backups; and monitor logs weekly.

A VPS is not automatically more secure than shared hosting. Shared hosts patch the OS, run WAF rules, and manage the PHP stack for you. On a VPS you own every layer from SSH to MySQL. A properly hardened VPS with isolated resources, key-only SSH, and UFW is safer than overcrowded shared hosting where one compromised neighbour can affect others. An neglected VPS with default root passwords and open ports is far worse. Security on a VPS is entirely your responsibility, which is both the risk and the upside if you follow a layered hardening checklist.

1 GB RAM works for a small brochure site with caching. WooCommerce or page-builder sites need 2 GB minimum. Security tools like fail2ban and backup agents consume memory too.

Budget roughly Rs 3,000–8,000 per month (~USD 22–60) for a secured 2 GB VPS with backups and monitoring tools. Cheaper than one cleanup after a defacement incident.

A lightweight scanner or firewall plugin adds value for file integrity checks and login telemetry on a VPS, but it does not replace server-level controls. Prefer fail2ban, UFW, TLS via Let's Encrypt, and correct file permissions over a bloated all-in-one security plugin that slows every request. On production sites I maintain, fail2ban and rate limits at the web server layer stop most brute-force noise before a plugin ever runs. Use plugins for 2FA enforcement and checksum verification, not as a substitute for SSH hardening or off-site backups.

Start with a fresh Ubuntu 22.04 or 24.04 image and create a non-root sudo user such as deploy. Add your SSH public key to authorized_keys, set chmod 600 on that file, then edit sshd_config to set PermitRootLogin no, PasswordAuthentication no, and PubkeyAuthentication yes. Restart sshd after changes. Never run WordPress operations as root. Changing the default SSH port reduces log noise but is not real security on its own. Pair key-only authentication with fail2ban jails that watch auth.log for repeated failed login attempts.

UFW is simple and sufficient for most WordPress VPS setups. Set default deny incoming and allow outgoing, then allow OpenSSH and either Nginx Full or Apache Full depending on your web server. Enable UFW and verify with ufw status verbose. Do not expose MySQL port 3306 to the public internet on a WordPress VPS. If you use a control panel or remote database access, open those ports only from trusted IP ranges. This four-layer model starts at the network edge before any traffic reaches PHP or WordPress.

Both Nginx and Apache work with WordPress 7.1 on PHP 8.3 or 8.4. Nginx plus PHP-FPM uses less memory at idle, which suits 1–2 GB VPS plans, but requires manual rewrite rules instead of native .htaccess support. Apache with mod_php or PHP-FPM is faster to set up for .htaccess-heavy plugin stacks. Regardless of choice, install TLS with Certbot, run a dedicated PHP-FPM pool per site on multi-site hosts, and block PHP execution inside wp-content/uploads via server rules. Pick one stack and harden it consistently rather than mixing configs.

Wrong ownership is a top cause of hacks and update failures on VPS WordPress installs. Set ownership to deploy:www-data, directories to 750, files to 640, and wp-config.php to 640. Grant group write only where WordPress needs it: wp-content/uploads, cache directories, and sometimes wp-content/upgrade at 770. Never use 777 permissions. Move wp-config.php above the web root when your vhost document root allows it, so the file stays outside direct URL access even if rewrite rules fail. Block PHP execution in uploads via Nginx or Apache rules to stop uploaded shell files from running.

Add three constants before the stop-editing line in wp-config.php. DISALLOW_FILE_EDIT removes the theme and plugin editor from wp-admin, so attackers who steal an admin session cannot inject PHP through the dashboard. FORCE_SSL_ADMIN ensures admin traffic stays on HTTPS. WP_AUTO_UPDATE_CORE set to minor enables automatic minor core updates without waiting for manual intervention. Store database credentials only in wp-config.php, never in version control. When possible, place wp-config.php one directory above the public document root and update the require path accordingly.

Brute-force attacks against wp-login.php and xmlrpc.php hit every public WordPress install within hours of launch. Layer application controls on top of server hardening. Install a reputable 2FA plugin and require it for all administrator accounts. Use 20-plus character unique passwords, never reused from hosting panels. Add rate-limiting via a plugin or fail2ban jails on 404 and 403 patterns. Disable xmlrpc.php entirely in your Nginx or Apache config if you do not use the mobile app or Jetpack remote features. On a recent VPS migration I handled, fail2ban and rate limits absorbed a login flood on day three without emergency plugin installs.

Enable automatic minor core updates via wp-config.php so security patches apply without manual intervention. Check plugins and themes weekly using WP-CLI commands like wp plugin update --all. Major WordPress releases should go to staging first on WooCommerce or membership sites, where a plugin conflict during checkout costs real revenue. Zero-day plugin patches may need same-day production deploys, which is another reason off-site backups must work before you update. Delete inactive themes and plugins rather than leaving them dormant, because every installed plugin is additional attack surface.

Local snapshots on the VPS die with the server, so push encrypted backups to S3, Backblaze B2, or another region. Automate with WP-CLI and cron as the deploy user: export the database nightly, tar wp-content, then sync to your bucket with aws s3 sync. Schedule jobs in the early morning hours to avoid peak traffic. Test a full restore to staging every quarter, not just a diff check. A real rebuild confirms your backup chain works before ransomware, a zero-day plugin flaw, or a bad deploy forces you to recover under pressure.

No. Never expose MySQL port 3306 to the public internet on a WordPress VPS. WordPress should connect to a local database instance using a dedicated user with least privilege, not the MySQL root account. Create one database and one user granted only SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, and INDEX on that database. Use utf8mb4 for full Unicode including Nepali content. MySQL 8.4 LTS or MySQL 9.7 both work. Store credentials exclusively in wp-config.php. If remote database access is genuinely required, restrict the port to trusted IP ranges through UFW rather than opening it globally.

Edit php.ini in your PHP-FPM pool, typically under /etc/php/8.3/fpm/php.ini on Ubuntu. Set expose_php Off, display_errors Off, log_errors On, and allow_url_fopen Off. Disable dangerous functions including exec, passthru, shell_exec, system, proc_open, and popen via disable_functions. Set upload_max_filesize and post_max_size to sensible limits such as 64M. Never run display_errors On in production because it leaks paths attackers exploit. Some backup and image plugins need specific functions, so test after disabling. Restart PHP-FPM after changes and verify the site still handles uploads and plugin operations correctly.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: