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


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. Configuring the domain in Cloudflare
  4. Install and configure Nginx Proxy Manager
  5. 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, so I honestly wouldn’t even know how 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 the 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:

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

Important 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 3000 for the web UI, 53 for DNS queries, 853 for DNS over TLS, etc. Check out the Docker Hub page for AdGuard’s image if you want to expose specific ports, or want to make other changes not covered here.

When ready, use command docker compose up -d to download and run the container as a daemon in the background.

In my case, the Flint 2 router automatically adds the necessary routing rules so that all devices on your network use AdGuard Home as their DNS server, but if running AdGuard Home as a Docker container you will need to configure it as the DNS server in your router. Each router is different, but generally you’re looking for the DNS server settings usually located within a router’s DHCP section. 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 — unfortunately AdGuard Home does not have the option to act DHCP (assuming your router let’s you turn off its own DHCP server) or else manually setting the AdGuard Home IP address as the DNS server on a per-device basis. If this is undesirable or unfeasable, you might consider using Pi-Hole instead since it can act as the DHCP server.

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.

Next, we’ll go into the AdGuard Home web UI and add the DNS records we need for Nginx Proxy Manager. It 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.

Configuring the domain in Cloudflare

Next, we’ll be using Cloudflare to get the TLS certificates for our custom domain. 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.

To add a domain to Cloudflare:

  1. Login to Cloudflare, go to Websites on the sidebar if you’re not already there, and click the Add a site button.

Adding a website to Cloudflare.

  1. Enter your domain and click Add site, scroll down and click on the Free plan at the bottom, and click Continue.

Adding a website to Cloudflare.

  1. After waiting a few moments for the DNS quick scan, you should see your domain’s DNS records appear. Click on Continue.

  2. Cloudflare will now present you with the URLs to two nameservers, should be something like adam.ns.cloudflare.com. Leave this page open, we’ll come back to it.

  3. Login to the registrar that owns your domain, go into your domain’s settings, and change the DNS nameservers to both of the URLs provided by Cloudflare.

I tend to use Namecheap, so I can tell you if your domain is with them, go to Domain List and click Manage next to the domain you want to add. Next to Nameservers choose Custom DNS from the dropdown list, add the two Cloudflare nameservers, and click the green checkmark to finish.

  1. Go back at your site’s Overview. If you still see Complete your nameserver setup, you can try using the Check nameservers button to see if it happens faster, but in the meantime, we need to do some additional setup.

  2. From your site’s Overview scroll down and you’ll see a section called API with a Zone ID and Account ID. Under that, click on Get your API token.

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

  4. Under Zone Resources, leave the first two dropdown menus as is and in the final dropdown select your domain, then click on Continue to summary and finally on the Create Token button.

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 HTTPS in the reverse proxy.

Install and congifure Nginx Proxy Manager

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

Create a compose.yml file, use the below as a base. (If you are also running Pi-Hole 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

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. 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.

Now run the command docker-compose up -d (using the -d flag has it run in the background as a daemon) within the same directory where the compose file is located to create the container.

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 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 (you should not encounter any issues as long as ports 53, 80 and 443 are not in use by another service) 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 only, leave the other two toggled 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.