Using Caddy as a reverse proxy in a home network

If you want to run a service inside a Local Area Network (LAN) such as your home or office – and especially if you want to be able to access it from outside that network – Caddy can help you accomplish this quite easily. This guide will show you how. It assumes you’ve never done this before, but that you have some technical proficiency and are somewhat knowledgable about your own network.

:warning: Do this at your own risk. There are several ways to make private services accessible from other networks, and this tutorial is just one way. No matter how you do it, setting up external access is risky and, if done wrong, can lead to compromise of your devices or information theft. You assume full responsibility when following this tutorial or doing anything like it. Even when using a VPN, you are making trust decisions that have serious implications, so be careful! (VPN traffic also “sticks out” more on the wire, and are more likely to be blocked in some corporate, political, or institutional settings.)

Definitions

You can skip this section for now and refer to it later if you have questions.

  • IP address - a unique address that refers to a device on a network. A device could have multiple addresses depending on where you are accessing it from (these “perspectives” are also known as address spaces):

    • Public IP - the unique address that refers to your LAN from the perspective of the WAN.
    • Private IP - the unique address that refers to a device on your LAN from the perspective of within that same LAN.
  • ISP (Internet Service Provider) - the company that gives you Internet access. They manage your physical link to the Internet and assign your LAN an IP address. They also have control over your Internet speeds and policies governing the use of that link including data caps. Some ISPs don’t actually give you a public IP address (for example, some ISPs give a whole apartment building one public IP and wire the whole building as a single internal network, with each apartment optionally making their own private networks using their own routers – in that case, your WAN address is not really public; also known as double NAT). If that’s the case, it might be tricky to use the method described here which uses port forwarding; consider a VPN instead.

  • LAN (Local Area Network) - the private network inside your home or office that only you should have access to and control over. Your computer, phone, and other devices physically (possibly wirelessly) connect to this network.

  • NAT (Network Address Translation) - the rewriting of IP addresses from one address space to another. For example, when a packet passes from your LAN to the WAN, the addresses are translated from private addresses to public addresses.

  • Port forwarding - the act of directly forwarding data packets from one interface (or physical link) to another. This is not really proxying; instead, think of railroad tracks: without port forwarding the track ends at a station, but with port forwarding the track seamlessly continues to the next station.

  • Router - in typical consumer setups, it’s the physical device that sits between the WAN and the LAN and routes data packets to their proper places. Technically this is distinct from a modem, switch, wireless access point, and firewall; but many consumer products bundle all those into one device commonly called a “router.” Your ISP might give you a router. A typical router has a WAN port where you plug “the Internet” into, and one or more LAN ports, which you plug your computers into. This is virtually (and physically) where the WAN/LAN (public/private) split happens. It’s also called the edge.

  • WAN (Wide Area Network) - the Internet (or “the outside world” or “external network”). Technically, it’s the link to the network next to your LAN. It’s how your LAN gets access to the Internet. In this tutorial, we assume that the WAN is the public Internet, i.e. that your ISP gives you a public IP address.

Requirements

You will need:

  • A domain name. They’re about $12/yr. Just get one. It makes security and access easier. You can also use a subdomain if you already have a domain.

  • Your public IP address. Your ISP assigns one that is either static or dynamic:

    • Static IP: Stays the same. This is preferred because there’s less to maintain. This tends to be a luxury among consumer ISPs.
    • Dynamic IP: Changes. This is still workable, but you’ll need to keep your DNS records updated every time it changes. Fortunately, this can be automated.
  • Access to your router’s control panel. This is usually a web UI at 192.168.0.1, 192.168.1.1, or 10.0.0.1 or similar. Consult your router’s documentation.

  • Caddy as a reverse proxy and TLS terminator. You should know how to use it.

  • One or more services you want to run and make accessible. Usually these run on a specific port; you will need to know what each one is. You’ll also need a computer that’s on pretty much all the time so you can access them whenever you want.

Topology

The way this usually works is you’ll leave your router at the edge of your network, and all devices (and services you want to host) are safely behind the router. That’s the normal setup. Then you’ll configure port forwarding on the router to the reverse proxy (Caddy). From there, Caddy can route HTTP requests to various services. Here’s what it looks like when completed:

DNS

First, set up your domain name. This is easier than you think: log into your domain registrar and configure an A record with the value being your public IP address. For example, in the diagram above, if your public IP address is 63.121.32.59, you would set that value for the A record.

After that takes effect (might be a few minutes to a few hours), you should be able to access your router from anywhere without having to remember its public IP address: just type the domain name instead. Of course, if your IP is dynamic, you will have to keep it updated.

It is crucial you configure DNS properly so that Caddy can serve your site over HTTPS with a publicly-trusted certificate.

Services

If you haven’t yet, make sure your services are up and running internally. We’ll connect them to the outside later. In the diagram above, this would be Service A and Service B, although how many and what they are is up to you.

Most home server setups run all their services on a single machine which is what we’ll assume in this guide. If you’re using multiple physical machines, Docker, or any virtualization, then the necessary adjustments to ports and addresses are up to you! I like to keep things simple.

Port forwarding

Before we start running Caddy, let’s get ready by forwarding a port. This will allow services to be accessible from the outside and for Caddy to obtain and renew publicly-trusted TLS certificates which are vital to encrypt connections.

Since we assume you’ll access your services over HTTPS, we can forward the default HTTPS port (:443). Or, you can forward any other (high, unused) port. If you use any port other than :443, make sure to specify the port explicitly in any URL (e.g. https://example.com:1234) so your clients know which port to use. In this tutorial we’ll forward :443 to Caddy at 192.168.0.2:443 (but the actual internal IP address depends on your network, of course).

So, go ahead and configure that in your router now. For example, if Caddy will be running on your server at 192.168.0.2:443, then you would forward WAN port 443 to 192.168.0.2:443. Your router might give you the option to forward TCP, UDP, or both. Go ahead and forward both, especially if you want to support HTTP/3, but if UDP is problematic for you then switch to just TCP.

Do not forward a port directly to your running services. They should not be exposed directly to the Internet. Be sure nothing is running at the destination address. It’s mostly safe to forward the port if there is nothing listening, but if something is inadvertently listening you could be breached in the meantime. Never forward a port and forget about it! To clarify, you will be forwarding only 1 port for this tutorial.

Let’s talk about security real quick before we go any further.

Security

The Internet is untrusted, so we need to defend our network. Without port forwarding, the router kind of acts like a firewall: no connections from the outside can easily make their way into your devices sitting on your LAN. This is good. By default, routers (should) drop incoming connections from the Internet on the floor. Other than this being the secure default, it’s also because it’s not obvious where those incoming connections should go. A router is like a railroad junction: from one incoming track, there are multiple possible destinations, so which one does it choose? You have to configure that. This is where port forwarding comes in. Most consumer routers provide only basic functionality for this, but it is probably sufficient for our requirements.

:warning: If you enable port forwarding from the router to any device on your LAN, any untrusted clients from the Internet can connect directly to your internal devices on the ports you forward. This is usually not good, unless you have other things in place to enforce authentication, rate limiting, blocking abuse, etc. Once you forward a port, it’s like opening a firehose. Expect the worst things in cyberspace (yes I just used that word) to flood your devices as soon as you forward a port.

When forwarding a port, there are two important features you’ll need for a basic defense:

  1. Encryption (TLS). This is powered by Caddy. As long as you configured your DNS properly above, Caddy serves your site over HTTPS by default. Encryption allows anyone and everyone to access your network privately and securely.

  2. Authentication. This may be powered by Caddy, or you may delegate to individual services. Authentication ensures that only you can access your network, and usually it requires encryption for it to be safe.

Note that most consumer routers will not provide either of these for you – they are more like firewalls at the IP/link layers. Caddy encrypts connections, so you’re good there :white_check_mark:. For authentication, you have a few options. Caddy can provide basic authentication as well, or you can set up another authentication module, or you can let the backend services do their own authentication.

Because authentication is like a gate, if you let the reverse proxy (Caddy) do authentication too, you can better hide which service(s) you are running behind it. Otherwise if you let individual services do authentication, their login screens can obviously reveal that you are running that service. This might not be a problem for you; it just depends what you want.

There are other measures you might consider taking to protect your network: for example, rate limiting and blocking abusive client IPs (or allowing only trusted IPs). Caddy can help with these too. You can also use tools like fail2ban to block clients who fail authentication too many times.

Your security is your responsibility. When you are ready, continue.

Reverse proxy

We’re now forwarding everything from :443 to 192.168.0.2:443 (actual IP varies), so let’s set Caddy up on machine 192.168.0.2. Caddy uses port 443 by default when we give it a domain name.

Please verify your DNS is set up properly and also ensure your port is forwarded properly. If not, Caddy will not be able to obtain a TLS certificate, and trying too many times could rate-limit you by the CA. This is the #1 most common error.

Install Caddy as a system service. If you downloaded Caddy through a package manager, then this might already be done for you.

Once Caddy is running as a service, you can change the config file to be a reverse proxy. There is no 1 config that works for everyone, so please, customize this as necessary:

example.com

reverse_proxy /service-a/* 192.168.0.2:8001
reverse_proxy /service-b/* 192.168.0.2:8002

Obviously, replace example.com with your domain name. We configured two proxy rules: one for /service-a/* and one for /service-b/*. You might have other ways to match requests that work better for your setup. Please adjust accordingly. You might also have just 1 service to proxy to, in which case you could simply use a single line like reverse_proxy 192.168.0.2:8001.

If your services are running on the same machine as Caddy, you could even loop back directly with 127.0.0.1 or localhost instead of using the internal IP address.

Apply the new configuration and be sure to check the logs for errors. Do not stop and restart the server. If Caddy has trouble getting a certificate, it will intelligently back off and retry, but you should fix the problem ASAP. If you do get certificate errors, switch to a test/staging CA endpoint to avoid rate limit problems.

If you want Caddy to do some basic authentication, just add a basicauth directive with a strong password. Without authentication (including that of any backend services), your server is freely accessible to anyone on the Internet, so, yikes.

Trying it out

Make sure to test that it works, both inside and outside your network. (Using your phone’s cellular connection is an easy way to have an outside vantage point.)

If, from within your own network, you cannot access your server, but you can from the outside, then it is likely your router does not support hairpin NAT (or does not have it enabled). I’ve encountered this problem with multiple ISP-provided routers and fixed it by running my own DNS server that resolves my public IP address to an internal address (and gives me redundancy against DNS provider outages too).

3 Likes

This is an excellent wiki article. I’ve picked up a couple of things I wasn’t aware of previously, for instance, the application of basicauth.

Am I correct in saying that if I want to use Caddy to manage the dynamic DNS and split-DNS issue, I have to use native JSON :cold_sweat: rather than Caddyfile? To date, I have used DNS-O-Matic to handle the dynamic DNS issue and configured DNSMasq as an internal DNS resolver to resolve the public domain name to the Caddy address.

It can be a bit tricky getting things working on the external facing side of the router. There are a couple of other ‘gotchas’ that may be worth mentioning somewhere in the article:

  1. Double-NAT and Carrier-Grade NAT can make it difficult, if not impossible, to address the dynamic DNS issue. This issue is not necessarily limited to apartment buildings. For instance, in Australia, how fibre is delivered to the home can force a double-NAT situation that is independent of the ISP as the broadband network is owned by a separate entity (the NBN). When it becomes all too hard trying to address the dynamic DNS issue, the only viable solution then is to resign yourself to acquiring a static IP address from your ISP, usually at an additional, recurrent cost.

  2. Some ISPs block ports 80 and 443 and other inbound ports by default. Chat to your ISP to see if these can be unblocked.

This Plex support article Troubleshooting Remote Access discusses these and some other common problems in setting up remote access. Some of this may also be relevant here.

FreeNAS users who would like to set up a Caddy reverse proxy should consider @danb35’s Reverse Proxy using Caddy (with optional automatic TLS) on the FreeNAS Community Forum. It’s a well supported community resource that’s been available since April 2019 and has recently been upgraded to use Caddy V2.

2 Likes

Yep. The built-in Caddyfile is only for the HTTP server because that is the most common use case. The JSON isn’t that scary though. Just take your Caddyfile, run caddy adapt, then go from there. I work with JSON configs all the time, even by hand, so it’s definitely doable. There’s even editor plugins to give you auto-complete and suggestions as you type! See Getting a better experience with JSON/YAML configuration

You can manage the dynamic DNS and personal DNS server differently, of course, it’s up to you. But Caddy can do it all if you want to (that’s what I do).

(To clarify, CoreDNS is not a Caddy 2 plugin, unfortunately it’s a totally separate fork of Caddy so you have to run that separately, until somebody writes a Caddy 2 module for it.)

All your other points are very good, thank you for bringing them up. They will be useful to some readers, I’m sure!

1 Like

Just wanted to add that duckdns (duckdns.org) is probably better for this type of project. It will give you a free dns and it can be encrypted with ex let’s encryption for https. For free of charge.

A post was split to a new topic: Presenting different content based on origin of request