I recently moved my main site from humfurie.org to humfurie.com. It sounded like a simple rename. It turned out to touch DNS, dynamic DNS, Cloudflare caching, my reverse proxy, my DNS filter, and nearly every service I self-host. Never have I ever thought it would be this problematic, so here is a write-up of what changed, what I had to update, and what to check if you ever do the same.
What changed
Two domains changed roles at the same time, which is what made this more complicated than a normal migration:
humfurie.org became humfurie.com. This is now my main site and portfolio.
humphrey.humfurie.org became humfurie.org. This is a separate website: a Laravel and React 12 app that I'm using as a side project to learn Laravel.
So humfurie.org did not simply disappear. It now points to a completely different site than before, and that matters for things like caching, which I'll get to below.
Step 1: DNS records and dynamic DNS
The first job was adding all of my domains and subdomains to the DNS records. Every service I expose has its own hostname, so each one needed a record under the new domain.
I also set up dynamic DNS (DDNS) using the oznu image in Portainer. DDNS is what keeps a DNS record pointed at your home connection when your public IP address changes. Without it, a self-hosted setup can silently break whenever your ISP hands you a new address.
Step 2: Switching every service on a subdomain
Anything running on one of my subdomains had to be moved over to the new domain. The list was longer than I remembered:
My portfolio and the new humfurie.org site
Immich
AdGuard
Outline
n8n
The humfurie.org API
My Resend instance
The Cordova site
LibreChat
My R2 instance
Each of these has its own configuration that refers to its hostname, so none of them could be treated as "set and forget" after the DNS change.
Step 3: Redoing the Cloudflare cache
After the switch, pages stopped loading properly. The cause was caching: Cloudflare was still holding on to content and settings from the old setup. I had to redo the caches to get things loading again.
I also had to redo the caching for humfurie.org specifically. Because it is now a different website from the one that used to live at that address, the old caching behavior no longer made sense for it. If you ever reuse a domain for a new site, expect to deal with this.
Step 4: Updating code and MCP connections
On the code side, I modified my portfolio to use humfurie.com. I also updated my Claude MCPs, since my personal services are now reachable on a different domain and the old URLs no longer applied. Once that was done, I pushed every change.
Step 5: The server side (this was not the end)
I assumed that pushing the code meant I was finished. I wasn't. The deployed services on my self-hosted server still needed the new domain too:
I connected to the server over SSH and pulled every service.
I updated the .env files, which hold the domain settings each service reads at startup.
I updated Traefik, my reverse proxy. It routes incoming requests by hostname, so a domain change means a lot of changes there as well.
I updated AdGuard, because I use its DNS functions and those entries needed to match the new domain.
Step 6: The warning I still can't explain (local or global?)
Just when I thought everything was working, a new problem showed up. Some of my subdomains (n8n, outline and cordova) are showing up as unsafe, and my browser says the connection is not private. This one is still open, and honestly I don't know yet whether it is a local problem on my own network or a global one that everyone sees.
Here is what I've found so far, and why it doesn't add up:
Google's Safe Browsing Site Status says the site is safe, yet the warning still appears.
The SSL certificate looks fine. Checking n8n.humfurie.com from my Mac showed a valid Let's Encrypt certificate issued on the day of the switch, with the correct hostname. So it doesn't look like a broken certificate, at least on that path.
My DNS is returning strange answers. Looking at AdGuard's query log, the addresses coming back are 0.0.0.0 or 192.168.0.0, when my server should be answering with 192.168.1.1. As far as I can tell it's happening for every site, not just mine, which points at my DNS setup rather than at Google.
My current suspicion is AdGuard, since I'm using its DNS functions and I changed a lot of it during the migration. A DNS filter that returns a blank address would send a browser to nothing, and the certificate warning could be a side effect. But I haven't confirmed that, and I can't rule out that part of it is a real reputation problem on the new domain. The next steps are to check what the query log says about why a query was answered that way (a block, a rewrite, or an upstream answer), compare the answers from AdGuard, my router and a public resolver like 1.1.1.1, and test the same sites from my phone on mobile data to see whether the problem follows me off my network.
I'll update this section once I know whether it's local or global.
A checklist if you ever switch domains
List every service that uses a hostname on the old domain before you start.
Add all new domains and subdomains to your DNS records, and set up DDNS if your IP changes.
Purge and rebuild CDN caches, especially if an old domain now serves a different site.
Update every app config and .env file, not just the code in your repositories.
Update your reverse proxy routing rules.
Update any local DNS or filtering rules that reference the old names, and test them from a device outside your network too.
Update integrations that call your services by URL, including tools like MCP connections.
Verify the new domain in Google Search Console early, so you can see security warnings and request reviews if a subdomain gets flagged.
Final thoughts
Most of the setup is running on the new domains, but it took far more steps than I expected, and the warning in Step 6 is still a loose end. The main lesson is that self-hosting means you own every dependency, including the ones you stop thinking about, like DNS, caching, and routing. If you plan a migration like this, write the checklist first, and test from outside your own network as well as inside it. I'll keep adding more details to this post as I remember them.