Reverse Proxying WordPress from a Home Server

I have an older Ryzen 5 3600 with 64 GB of RAM and NVMe storage sitting in the basement. I also have a Linode VPS with 1 vCPU and 2 GB of RAM — modest by cloud standards, but plenty for what it needs to do for my use case. The basement machine is bored, and I wanted to see what happened when a WordPress site had access to real hardware instead of shared CPU credits.

To be clear: this is a playground project. My production sites live on the VPS, where they benefit from redundant power, multiple network uplinks, and a data center SLA. That VPS — 1 vCPU, 2 GB of RAM — handles the full stack just fine with the right tuning. But the experiment answers a different question: what if the VPS didn't have to run PHP or MariaDB at all? Strip it down to nginx, rsync, and a Tailscale client, and even a $5 Linode Nanode with 1 GB of RAM could do the job. The home server is for experimenting. The architecture turned out well enough to document, and the principles apply whether you're tinkering or building something you intend to serve real traffic to. Just know the difference between a weekend project and a production workload before you point a domain at your basement.

The architecture puts the entire PHP stack — nginx, PHP-FPM, MariaDB — on the home server. The VPS acts as a reverse proxy. Static assets are mirrored to the VPS via rsync so they never touch the home connection. Every database query runs at local NVMe latency. The only thing crossing the internet is a single HTTP request and its response, encrypted over Tailscale's WireGuard mesh.

What you need

  • A VPS with a public IP — Linode, Vultr, DigitalOcean, any provider works. The smallest plan is plenty
  • A home server or powerful desktop running Linux — the architecture assumes nginx, PHP-FPM, and MariaDB on the backend
  • Tailscale installed on both machines — free for personal use, creates a WireGuard mesh network between them
  • A domain name pointed to the VPS, with SSL certificates via Let's Encrypt
  • SSH key access to both machines

If you're already running Cloudflare in front of the VPS, the Cloudflare Edge Configuration guide covers the transparent proxy setup — this article assumes Cloudflare is already in place or that you'll add it later. Either way, the architecture works the same.


The architecture

Browser
  ↓
Cloudflare (transparent edge — DDoS shield, CDN cache)
  ↓
Linode VPS (1 vCPU, 2GB RAM, SSD)
  ├── Static assets: served from local disk (rsync mirror)
  └── Dynamic requests: proxied over Tailscale
        ↓
      Home Server (Ryzen, 64GB RAM, NVMe)
        ├── nginx (HTTP only — Tailscale encrypts)
        ├── PHP-FPM (static pool, JIT enabled)
        └── MariaDB (buffer pool tuned for available RAM)

One request, one WAN hop. The browser hits Linode. Static files — images, CSS, JavaScript, fonts — return instantly from Linode's disk, no home connection involved. PHP requests make exactly one round-trip over Tailscale to the home server. MariaDB never leaves NVMe — PHP talks to it at localhost latency. That's the entire architecture.


Why proxy the request and not the query

The naive approach — keep PHP-FPM on the VPS, connect to a remote MariaDB on the home server over Tailscale — fails under real load. A single WordPress page issues 30 to 50 database queries. That's 30 to 50 round trips over the internet, each one paying WireGuard encryption overhead, ISP latency, and TCP connection setup. Even on fiber, the cumulative delay is visible. On a page with a few plugins active, you're looking at a second or more of database latency before the first byte of HTML reaches the browser.

Moving the entire PHP stack to the home server inverts the problem. Every database query runs at local NVMe speeds — sub-millisecond. The only thing crossing the WAN link is one HTTP request and one HTTP response. The database never leaves the machine it lives on.


Step 1: Tailscale — the network layer

Tailscale provides a WireGuard-based mesh network. Each device gets a static private IP in the 100.x.x.x range and an optional MagicDNS hostname. Traffic between nodes is encrypted end-to-end. No port forwarding on the home router, no dynamic DNS, no exposing SSH to the public internet.

Install on both machines:

# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh

# Authenticate and connect
sudo tailscale up

Once connected, note the Tailscale IP of the home server — you'll use it in the VPS proxy configuration. It stays static as long as the machine stays connected to your Tailscale network.

Why HTTP between proxy and backend: Tailscale already encrypts the wire. Adding a second TLS layer inside the tunnel would add negotiation overhead with no security benefit — the traffic is already encrypted by WireGuard before it leaves either machine. The home server's nginx listens on port 80, bound to its Tailscale IP and localhost only.