Setting up a reverse proxy for HTTPS with a custom domain using Nginx Proxy Manager, AdGuard Home and Cloudflare


updated

I've used a reverse proxy to access my self-hosted apps and services for years, and used Pi-Hole as my home network DNS for even longer, but recently switched to AdGuard Home. That meant redoing all my DNS records within AdGuard so I could get my reverse proxy back up and running, and I decided to write down the steps I took. When done, we'll be able to access our apps and services through a custom domain, with unique sub-domains for each app or service, with full HTTPS and accessible only locally.

Sections

  1. Pre-Requisites and Caveats
  2. Setting up AdGuard Home as network DNS server
  3. Adding DNS rewrites in AdGuard Home
  4. Add a domain in Cloudflare
  5. Install and configure Nginx Proxy Manager
  6. References

Pre-Requisites and Caveats

I wrote previously about how to set this up using Pi-Hole, but I recently bought a GL.iNet Flint 2 router which has AdGuard Home built-in, so it seemed a waste not to use it. (Also for as great as Pi-Hole is, I have had to redo it multiple times over the years due to database errors or just a dead mini SD card or USB drive, etc.) So, this guide will be mostly based on my old one, with just the parts dealing with Pi-Hole replaced with AdGuard Home, since setting up Nginx Proxy Manager and Cloudflare work the same as always.

This guide uses specific third-party services, namely Cloudflare, AdGuard Home and Nginx Proxy Manager to set up a secure local-only reverse proxy. The same is possible with other tools, apps and services including Pi-Hole (which as I mentioned, I previously used for many years) or NextDNS instead of AdGuard Home, Caddy or Traefik instead of Nginx, any other DNS provider instead of Cloudflare, etc. I’m only writing about my preferred tools that I’ve used multiple times to set everything up and keep it running for over a year.

This guide will require a owned custom top-level domain (TLD), such as a .com or .cc or .xyz, etc. Certain TLDs can be bought for super cheap on Namecheap or Porkbun, but be aware in most cases after the first year or two, the price will see a steep jump. I again prefer Cloudflare for purchasing domains, since they always price domains at cost, so you won’t see any surprise price hike one year to the next. An alternative I won’t be getting into is using dynamic DNS, as I’ve not had to use it myself, so I honestly wouldn’t even know how to begin to set that up.

Setting up AdGuard Home as network DNS server

In my case, AdGuard Home already comes installed and ready to use on the GL.iNet Flint 2 router, requiring only toggling it on from the router’s web admin UI to activate. If you want to run it bare metal on a server, see their getting started guide. If you want to run AdGuard Home in a Docker container, this compose.yaml should work for you as a base.

Information

Please note, I have never run AdGuard Home in a Docker container myself, I’m just using common sense defaults to get up and running fast. Using network_mode: host allows all necessary network ports, including 53 for DNS (which must be available for DNS resolution to work), port 3000 for the web UI, , 853 for DNS over TLS, etc.

Check out the Docker Hub page for AdGuard Home if you want to expose specific ports, or want to make other changes not covered here.

services:
  adguardhome:
    image: adguard/adguardhome
    container_name: adguardhome
    network_mode: host
    restart: unless-stopped
    volumes:
      - /local/path/adguardhome/work:/opt/adguardhome/work
      - /local/path/adguardhome/conf:/opt/adguardhome/conf

When ready, use command docker compose up -d to download and run the container as a daemon in the background. (If you’re already running a stack of containers, you can add the above to your existing compose file.)

Next you’ll need to set the IP address of AdGuard Home as the DNS server in your router. (If you’re setting this up on the Flint 2, skip this part because it’s automatically configured.) Each router is different, but generally you’re looking for the DNS server setting, usually located within a router’s DHCP settings. If your router lets you set a custom DNS server, enter the IP address of the machine running AdGuard Home here, e.g. 192.168.0.50.

However, not all routers let you set a custom DNS server (this is especially common in ISP-provided routers), in which case you are out of luck — you will have to manually set the AdGuard Home IP address as the DNS in network settings on a per-device basis. If this is undesirable or unfeasable, and if your router lets you turn off it’s DHCP server, you might consider using Pi-Hole instead since it can act as a DHCP server for your network.

Once AdGuard Home is being broadcast as the network’s DNS server by the router, your devices will gradually begin querying it as they renew their DHCP leases. You can usually force a renew by restarting a device, or just reboot the router and it should propagate the changes to all devices.

Add DNS rewrites in AdGuard Home

Next, we’ll go into the AdGuard Home web UI and add the DNS rewrites we need for Nginx Proxy Manager. The web UI should be accessible via your browser at http://<ip-address>:3000.

  1. In the AdGuard Home web UI, click on Filters on the navigation bar at the top, and choose DNS Rewrites from the dropdown menu.

  2. Click on the Add DNS rewrite button.

  3. Add a your domain as a wildcard like *.domain.com, and under that the IP address of the server running your services, e.g. 192.168.0.50, then click on Save.

Information

If you want to proxy services from more than one server, create specific entries for each one rather than using a wildcard — for example, service1.domain.com would point to 192.168.0.50, service2.domain.com would point to 192.168.0.100, etc.

This is also the case you want to proxy something straight to domain.com rather than a sub-domain, make sure to create a specific DNS rewrite for it, e.g. domain.com pointing to 192.168.0.200.

That’s all you have to do with AdGuard Home. Next, we’ll be using Cloudflare to get the TLS certificates for our custom domain.

Add a domain in Cloudflare

You’ll need to create a free account on Cloudflare. (Feel free to use another DNS provider if you prefer.) You can add a domain bought from another registrar to Cloudflare by following the below instructions, or if you purchase a domain on Cloudflare it will automatically be configured and you can skip this section.

To add an existing domain to Cloudflare:

  1. On the Cloudflare dashboard Account Home, click the + Add a domain button.

Adding a domain to Cloudflare.

  1. Enter your domain, leave Quick scan for DNS records selected, and click Cotinue.

  2. Click on the Free plan at the bottom and click Continue.

Cloudflare free plan.

  1. You’ll see your DNS records, if there are any. Don’t worry about this right now and click on the Continue to activate button.

DNS management page.

  1. You’ll see a pop-up window saying you should set your DNS records now, click on Confirm.

Add DNS records pop-up.

  1. You’ll be provided some instructions to update the nameservers on your domain’s registrar, open a new tab and follow those instructions. Once you’ve added the Cloudflare nameservers at your registrar, go back to Cloudflare and click on Continue.

  2. Now you’ll have to wait a few minutes for the changes to propagate, then click on Check nameservers and reload the page. If it’s still shows Pending next to the domain at the top, just keep waiting. In the meantime, we need to do some additional setup.

  3. From your domain’s Overview scroll down and you’ll see a section at the bottom-right called API with a Zone ID and Account ID. Under that, click on Get your API token.

  4. Click the button Create Token, then click the Use template button next to Edit DNS Zone.

  5. Under Permissions, leave the first entry as is, click on +Add more.

  6. For the new Permission, choose in order from the dropdown menus Zone, Zone and Read.

  7. Under Zone Resources, leave the first two dropdown menus as is, and in the final dropdown all the way to the right, select your domain. Scroll past everything else,without changing anything else, click on Continue to summary, and finally on the Create Token button.

  8. On the next page you’ll see your API token, make sure to save it somewhere because it will not be shown again. We will need this API token for to provision the TLS certificates in Nginx Proxy Manager.

  9. Once your domain is Active in Cloudflare, you can move on to the next section.

Install and congifure Nginx Proxy Manager

If you don’t have Docker installed already and need to do it from scratch, I suggest using Docker’s own install script by running the command curl -fsSL get.docker.com | sudo sh. I’ll be using docker compose to install and run Nginx Proxy Manager, it’s the easiest way to run long-term containers.

Create a compose.yml file, use the below as a base. (If you are also running AdGuard Home as a container, I’d suggest putting them both on one compose file.)

services:
  nginx-proxy-manager:
    container_name: nginx-proxy-manager
    image: "jc21/Nginx-proxy-manager:latest"
    ports:
      - 81:81 # web UI port
      - 80:80
      - 443:443
    volumes:
      - /opt/docker/nginx:/data
      - /opt/docker/letsencrypt:/etc/letsencrypt
    restart: unless-stopped
Information

Make sure that ports 80 and 443 are available on your server and not being used by anything else! Nginx Proxy Manager needs port 80 for HTTP and port 443 for HTTPS.

This compose file uses bind mounts to store container data in specific directories on the host, as I find this easier to migrate than volumes. (If you save your site on GitHub you can just clone it and install it anywhere.)

Be sure to type in your own local path to where you want the data from Nginx Proxy Manager to live in your server, e.g. /home/bob/docker/.. etc. and run the following command within the same directory where the compose file is located:

docker compose up -d

If you are running Portainer and want to create the container(s) from within it’s UI — rather than creating the compose file and using commands in the terminal — do the following:

  1. In the Portainer web UI, go into your environment and click Stacks from the sidebar.

  2. Click the + Add Stack button at the top-left. Name the stack, copy and paste the contents of the compose.yaml above into the web editor.

  3. Once done, scroll down and click the Deploy the stack button.

Whichever method you use, wait a few moments while the image is downloaded and the container is created. Once it’s up and running we can login to the Nginx Proxy Manager web UI at http://<ip-address>:81 where the IP is the server running Nginx Proxy Manager.

Nginx Proxy Manager login screen.

Go into the Nginx Proxy Manager web UI at http://<your-ip-address>:81, login with the default email [email protected] and password changeme, and as soon as you login go to Users on the nav bar, and change (ideally) both the email and password of the administrator account.

To add proxy hosts click on Hosts on the navigation bar at the top, then click the Add Proxy Host button.

We’ll create an entry for Plex first, which is running as a container on the same host at port 32400. You’ll begin in the Details tab.

Creating a proxy host.

  1. Under Domain Names type in *.domain.com and click the Add *.domain.com dropdown that appears. Make sure to include the * as this will create a wildcard certificate for use with all subdomains.

  2. Leave Scheme as http.

  3. For Forward Hostname/IP type in your server IP.

  4. For Forward Port type in 32400.

  5. Toggle on Websockets Support and Block Common Exploits, but leave caching off.

  6. Go to the SSL tab, click under SSL Certificate and select Request a new SSL Certificate from the dropdown.

Configuring SSL on proxy host.

  1. HTTPS should work with Force SSL toggled off, but feel free to toggle it on if you prefer.

  2. Toggle on Use a DNS Challenge, then under DNS Provider choose Cloudflare from the dropdown.

  3. Under Credentials File Content you’ll see see dns_cloudflare_api_token= followed by numbers. Replace these numbers with your Cloudflare API token.

  4. At the bottom, type an email address (you’ll get emails when your certificate is about to expire), toggle on that you agree to the Let’s Encrypt TOS, and click Save.

Assuming you set up and entered your Cloudflare API token correctly, after a minute or two an SSL certificate will be provisioned and the proxy host will be created. Now you should be able to go to https://plex.domain.com and see your Plex UI with full HTTPS.

To add additional proxy hosts, repeat the process as above (changing the forward port to the one used by each specific app you are proxying), but when you get to the SSL tab choose your now existing *.domain.com certificate from the dropdown, then proceed to choose Cloudflare as DNS provider and enter the API token. This process has to be done each time you add a new proxy host.

Always make sure the full URL you want to use (subdomain.domain.com) is added to the CNAME Records in Pi-Hole (or whatever DNS server you use in your home network) pointing to the server running Nginx Proxy Manager as target.

If something does not work as intended (503 error or the like), fiddle with the proxy host options — try both http and https scheme, try toggling Force SSL on and off, double-check your API token is correct, etc. You can also check the Nginx Proxy Manager container logs with the terminal command docker logs nginx-proxy-manager. (Or whatever container_name you used in the compose file when creating the container.)

Barring any errors, once you set up all your proxy hosts in Nginx Proxy Manager you should have full HTTPS when going to your services via https://subdomain.domain.com.

Information

One last thing, if you want to want to access the AdGuard Home web UI via HTTPS as well, be sure to use its default HTTPS network port 3001 in Nginx Proxy Manager.