Securing a Home Server with Cloudflare – Hide Your IP and Block Attacks

Run a blog on a home NAS and anyone can look up the domain and get your home IP, and with it the NAS login page that sits on the same connection. My NAS auto-blocked about 17,900 IPs over two years (the attack data). This is the order in which I used free Cloudflare to hide the home IP, limit the admin pages by country, block xmlrpc and restore real visitor IPs.

At a glance

  1. Switch SSL to “Full (strict)”, then set every DNS record to Proxied (orange cloud): the home IP disappears
  2. Turn off Bot Fight Mode and AI bot blocking, to keep Jetpack connected and stay visible in AI search
  3. One rule so that every subdomain except the blog (NAS admin pages and so on) is only reachable from my country
  4. One rule so that only Jetpack (ASN 2635) can reach xmlrpc.php
  5. Make the NAS’s nginx and WordPress see the real visitor IP, so auto block keeps doing its job
Diagram of the home server behind Cloudflare: visitors, Cloudflare, home router, NAS, Docker container and data
Cloudflare stands in front of the home server like a shield.

1. Hide the home IP with the Cloudflare proxy

Even with your nameservers at Cloudflare, a record set to “DNS only” (grey cloud) still shows your home IP. Only the proxy (orange cloud) makes visitors see Cloudflare’s addresses instead. The order matters.

  1. In SSL/TLS → Overview, set the encryption mode to Full (strict) first.
  2. In DNS → Records, switch the root (@), www, wildcard (*) and every other subdomain record to Proxied.
  3. Run dig +short yourdomain in a terminal and check that only Cloudflare addresses such as 104.x or 172.6x come back.

If you skip step 1 and leave it on “Flexible”, Cloudflare talks to your server over http, WordPress redirects back to https, and you get an endless redirect loop. “Full (strict)” needs a publicly trusted certificate on the server; I already had a ZeroSSL wildcard certificate, so it worked right away.

If even one record stays grey, that name gives your home IP away. If you have records for services that can’t be proxied, such as mail or a VPN, check those names separately.

When I moved the blog from Cafe24, a Korean managed WordPress host, to the NAS in 2024, I pointed the domain straight at my home IP, so this was the first thing to fix.

2. Turn off two bot settings

  • Bot Fight Mode: when on, it can block requests from WordPress.com’s servers and break the Jetpack connection.
  • AI bot blocking: when on, your posts won’t show up in AI search such as ChatGPT or Perplexity. Turn it off if you want that traffic, and block only training crawlers in robots.txt if that’s your concern.

After turning both off, I requested pages as Googlebot, Bingbot, Naver’s Yeti and Daum (the two big Korean search engines), the ChatGPT and Claude search bots and PerplexityBot. All of them got through with a 200.

3. NAS admin pages: my country only

I share photos with my family, so I can’t close the NAS admin pages completely. Instead, every subdomain except the blog only opens from Korea. That alone filters out most of the automated attacks from abroad.

  1. Security → Security rules (on some screens, WAF → Custom rules)
  2. Create rule → Custom rules, with a name you’ll recognise
  3. Click Edit expression and paste the expression below, with your domain instead of example.com and your own country code instead of KR
  4. Action: Block, then Deploy (Save as Draft doesn’t apply it)
(http.host ne "example.com" and http.host ne "www.example.com" and ip.src.country ne "KR")

The blog (root and www) is left out of the rule, so crawlers from abroad, Googlebot included, still get in. After deploying it I tested from seven other countries (Canada, the Netherlands, Spain, Italy, Sweden, Indonesia and Iran): the admin pages returned 403 every time, and the blog returned 200 every time. When I travel abroad, I just switch the rule off for a while.

4. Only Jetpack may reach xmlrpc.php

The server logs showed a login attempt against WordPress’s xmlrpc.php about every 20 seconds. Two-factor authentication kept them out, but they kept eating server resources. Jetpack talks to WordPress.com through that same file, though, so you can’t simply block it. Following Jetpack’s own documentation, I let only Automattic’s network (ASN 2635) through.

(http.request.uri.path eq "/xmlrpc.php" and ip.src.asnum ne 2635)

If you can’t use the ASN field, you can write Jetpack’s published IP ranges instead.

(http.request.uri.path eq "/xmlrpc.php" and not ip.src in {192.0.64.0/18 122.248.245.244 54.217.201.243 54.232.116.4 195.234.108.0/22})
Cloudflare's "Sorry, you have been blocked" page shown when opening xmlrpc.php
Opening xmlrpc.php from a normal connection now shows Cloudflare's block page.

Afterwards, Jetpack’s connection tests (including the WordPress.com-to-site check) all passed. (Jetpack IP allowlist guide)

5. Bring back the real visitor IP

With the proxy on, every visitor reaches your server from a Cloudflare IP. Leave it like that and two things go wrong:

  • If the NAS’s auto block bans a Cloudflare IP instead of the attacker, you and your family, coming in the same way, get locked out too.
  • WordPress comments, spam filtering and login protection can no longer tell real visitors apart.

Restore it on the NAS (nginx)

DSM’s nginx configuration already contains these lines. They mean “take the real IP from X-Forwarded-For, but read the list of trusted proxies from this folder”.

real_ip_header            X-Forwarded-For;
real_ip_recursive         on;
set_real_ip_from          127.0.0.1;
include     /var/tmp/nginx/trusted_proxy/*.conf;

DSM 6.2 has no screen that fills in that list, so the folder was empty. Put a file there with Cloudflare’s IP ranges and the NAS’s own address, and the blog, the NAS admin pages and the photo service all see real IPs.

# /var/tmp/nginx/trusted_proxy/cloudflare.conf (excerpt)
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 104.16.0.0/13;
# ... every IPv4 and IPv6 range from https://www.cloudflare.com/ips/
# + the NAS's own LAN IP (when traffic goes through DSM's reverse proxy)

/var/tmp can be wiped by a reboot, so I wrote a script that rewrites the file if it’s missing or different, tests the nginx configuration and reloads it, and put it in DSM’s Task Scheduler (as root, every 10 minutes). If the configuration test fails, it removes the file and goes back to how things were.

Once more in WordPress

In case the nginx part is ever missing for a moment, WordPress also has a small mu-plugin that uses the CF-Connecting-IP header as the real IP only when the request comes from a Cloudflare IP. Requests from anywhere else are ignored, so nobody can fake their IP with that header.

Checking it was easy: before, WordPress saw my IP as a Cloudflare address (162.159.x.x); afterwards it showed my real public IP.

The nginx trusted-proxy script (realip.sh) and the WordPress mu-plugin are on GitHub (dsm6-wordpress-homeserver). Change one line with your NAS’s address and it works on other DSM 6.2 boxes, and the refresh command pulls Cloudflare’s latest IP ranges.

6. Worth doing as well

  • Don’t use a default name such as admin for the NAS administrator, and turn on two-factor authentication.
  • Decide how many failed logins trigger the NAS’s auto block (I use three) and for how long.
  • It isn’t finished until you also block connections that go straight to your home IP and skip Cloudflare. Either allow ports 80 and 443 only from Cloudflare’s ranges in your firewall, or remove port forwarding entirely with Cloudflare Tunnel. Old DNS records may still remember your home IP, so changing your public IP at the end helps too.
  • If you need to manage the NAS from outside, don’t open the SSH port; come in over a VPN such as Tailscale.
  • The last line of defence is an off-site backup. If your security setup ever fails, a backup is the only way back.

Frequently asked questions

Is Cloudflare’s free plan enough?

For a personal blog, yes. The proxy, the certificate and custom security rules with country, path and ASN conditions: everything in this post runs on the free plan.

Won’t a country rule lock out search engines?

Not if the blog itself (root and www) is left out of the rule, as above. Crawlers from abroad still get in; only the non-blog subdomains are blocked.

Can I just turn xmlrpc.php off?

If you don’t use Jetpack, yes. If you do, allowing only Automattic (ASN 2635) is the safe way.

Can I keep using the NAS auto block after turning on Cloudflare?

Only if you restore the real IP (step 5). Otherwise, once a Cloudflare IP is banned, everyone coming through it is blocked.

This NAS runs WordPress on PHP 8.3 in Docker on DSM 6.2, and the numbers behind all this are in 17,864 IPs blocked in two years.

For the bigger picture, what it costs and what it’s like to run a blog from home, see Self-Hosting WordPress on a Home NAS – Two Years of Costs, Pros and Cons.

This is a translation of my original Korean article: 집 서버 보안 설정 – Cloudflare로 IP 숨기고 공격 막기. Last checked: 6 November 2026.

lavient Avatar

Author

lavient

A personal blog from Korea about everyday life and the things I care about, written from what I have actually used, visited and lived through.

Leave a Reply