How to Easily Set Up an HTTPS Reverse Proxy with Caddy for Beginners

Have you ever tried to set up a reverse proxy with Nginx and encountered issues? It happened to me frequently until I learned the intricacies. In this blog, I will demonstrate the easiest way to set up a reverse proxy with HTTPS using Caddy, an Nginx alternative. But first, what is a reverse proxy?

Reverse Proxy

A reverse proxy is a server that sits between a client and one or more web servers while intercepting connections from client to server. It can manipulate request data, load balance requests, protect from cyber attacks and many more!

Reverse proxy flow: traffic flows from user's device (D) to Internet to reverse proxy (E) to origin server (F)

Nginx Example

Let's take an example of a Nginx config file. The example shows a reverse proxy that listens on the default HTTP port 80, forwards requests to an upstream http://localhost:8080 and modifies some request headers along the way.

server {
    listen 80;

    server_name example.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

If you already know Nginx, this is the most basic reverse proxy configuration you can use. We define a few things here:

  1. server Block: This defines a virtual server. Multiple server blocks can route connections based on IP, port and server_name.

  2. listen: Port to listen to.

  3. server_name: Nginx matches the Host header of the connection to this and routes the request.

  4. location block: A location block is used to reach different resources in a server block.

  5. proxy_pass: This delegate requests or connections to one or more upstream servers.

  6. proxy_set_header: This directive is used to edit/add/remove headers to the upstream requests.

Generating SSL certificate

There are many Certificate Authority (CA) that issue SSL certificates. CAs are trusted issuers of digital certificates. If you ask how we determine which CAs are trusted, for most of us browsing the internet, It is the browser or OS vendor like Google, Microsoft, Mozilla, Apple etc. However, there is a governing forum called CA/Browser Forum that decides additional rules and regulations for CAs.

For our example, we can use a tool called Certbot, a tool from the Electronic Frontier Foundation that generates SSL certificates from a non-profit certificate authority called Let's Encrypt.

You can go to certbot.eff.org and install certbot for your system by following their guide. Once certbot is installed, just run -

$ sudo certbot โ€”-nginx

This will automatically verify your domain (you have to set your domain name to server_name), request a certificate from Let's Encrypt CA and modify your Nginx configuration to enable HTTPS for your server block.

Caddy Server

Just like Nginx, Caddy is a cross-platform, open-source web server. While Nginx is very fast and a big name in the industry, Caddy has some awesome features like -

  1. Automatic HTTPS: Caddy, by default obtains/renews SSL/TLS certificates from Let's Encrypt / ZeroSSL. Even for localhost or, internal addresses, Caddy can provision short-lived, auto-renewing certificates using a locally trusted certificate authority.

  2. Simple Configuration Syntax: Caddy's configuration file - Caddyfile is an extremely simple and human-readable format compared to the Nginx configuration syntax.

  3. Built-in static file server: Serve compressed files or compress on the go to save bandwidth. Serve static sites from a database, local file system or remote cloud storage. Also, serving a directory (that does not have an index file) looks like this:

    Caddy File Server

    To start a local file server, run caddy file-server in a directory that you want to serve.

  4. REST API for configuration: Caddy has a REST API that can be used to make dynamic configuration changes.

  5. Extensibility: Caddy has a huge library of pre built modules that can be used to supercharge it's capabilities.

  6. and many more...

Caddy Example

This blog is not a getting-started or how-to guide for Caddy, so I will stick to the very basic Nginx example and try to recreate that in Caddy, step by step.

  1. Install Caddy: Install caddy for your platform by following the official guide.

  2. Write Caddyfile: The Caddy configuration file is written in a file called Caddyfile, no extension. If we translate the same Nginx config above to Caddy in the simplest form, it will look like this -

     :80
    
     reverse_proxy http://localhost:8080
    
    ๐Ÿ’ก
    Caddy also has support for JSON configuration. For complex setups JSON is preferred.
  3. Run Caddy: From the same directory of the Caddyfile use caddy run to start caddy.

  4. Browse the page: Open http://127.0.0.1 in the browser. Caddy will now be proxying the traffic to http://localhost:8080.

Automatic HTTPS

To enable automatic HTTPS, we need to tweak the Caddyfile a little, by including a domain name -

example.com

reverse_proxy http://localhost:8080

If you run the above Caddyfile, caddy will try to generate a certificate for the domain example.com, and it will fail, as you do not own example.com.

๐Ÿ’ก
Example domains like example.com are only used for documentation purposes, and are not available to purchase or transfer.

If you change the domain name from example.com to localhost and then run Caddy, caddy will generate a new self-signed certificate for localhost and add it to the local trust store. So in the browser, it looks like this -

If you run this on a Cloud VPS that has a domain name pointed to it, with the domain in Caddyfile, caddy will provision a certificate from Let's Encrypt.

Request Header Manipulation

To do request header manipulation just like the Nginx config, there is a directive in caddy called header_up that adds/edits upstream request headers. The complete configuration after header manipulation for localhost looks like this -

localhost

reverse_proxy http://localhost:8080 {
    header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
    header_up X-Real-IP {http.request.remote.host}
    header_up Host {http.request.host}
    header_up X-Forwarded-Proto {http.request.scheme}
}

And for a domain that you own -

snehangshu.dev

reverse_proxy http://localhost:8080 {
    header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
    header_up X-Real-IP {http.request.remote.host}
    header_up Host {http.request.host}
    header_up X-Forwarded-Proto {http.request.scheme}
}

Production Deployment

It is recommended to run caddy in production with their official systemd Unit file(s).

In the official unit file, the default location of the Caddyfile is /etc/caddy/Caddyfile. So you need to insert your change to that file or edit the official unit file with your Caddyfile location to enable your configuration. Once the configuration is done, start/restart Caddy using systemctl -

$ sudo systemctl restart caddy.service

Conclusion

Caddy offers a streamlined and efficient way to set up a reverse proxy with HTTPS, making it an excellent alternative to Nginx for many use cases. Its automatic HTTPS feature, simple configuration syntax, and built-in static file server significantly reduce the complexity and effort required to manage web servers.

While Nginx remains a robust and industry-standard tool, Caddy's modern features and ease of use make it a compelling choice for developers looking for a hassle-free setup. Ultimately, the choice between Nginx and Caddy depends on your specific needs and preferences, but Caddy certainly stands out for its user-friendly approach and powerful capabilities.

Thank you for reading, and if you found this post helpful, please give it a ๐Ÿ‘, ask any questions in the comments, and share it with others who might benefit.

1
Subscribe to my newsletter

Read articles from Snehangshu Bhattacharya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Snehangshu Bhattacharya
Snehangshu Bhattacharya

I am an Electronics and Communication Engineer passionate about crafting intricate systems and blending hardware and software. My skill set encompasses Linux, DevOps tools, Computer Networking, Spring Boot, Django, AWS, and GCP. Additionally, I actively contribute as one of the organizers at Google Developer Groups Cloud Kolkata Community. Beyond work, I indulge in exploring the world through travel and photography.