# 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)](https://cf-assets.www.cloudflare.com/slt3lc6tev37/3msJRtqxDysQslvrKvEf8x/f7f54c9a2cad3e4586f58e8e0e305389/reverse_proxy_flow.png align="left")

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

```nginx
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](https://cabforum.org/) that decides additional rules and regulations for CAs.

For our example, we can use a tool called [**Certbot**](https://certbot.eff.org/)**,** a tool from the Electronic Frontier Foundation that generates SSL certificates from a non-profit certificate authority called [Let's Encrypt](https://letsencrypt.org/).

You can go to [certbot.eff.org](https://certbot.eff.org/) and install certbot for your system by following their guide. Once certbot is installed, just run -

```bash
$ 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](https://caddyserver.com/resources/images/file-browser/browse-themes.png align="left")
    
    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](https://caddyserver.com/docs/modules/) 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](https://caddyserver.com/docs/install).
    
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 -
    
    ```plaintext
    :80
    
    reverse_proxy http://localhost:8080
    ```
    
    <div data-node-type="callout">
    <div data-node-type="callout-emoji">💡</div>
    <div data-node-type="callout-text">Caddy also has support for JSON configuration. For complex setups JSON is preferred.</div>
    </div>
    
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 -

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

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Example domains like example.com are only used for documentation purposes, and are not available to purchase or transfer.</div>
</div>

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 -

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1722020001445/137a6a89-b9ca-42aa-b854-31ae8a856bd4.png align="center")

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 -

```plaintext
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 -

```plaintext
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](https://github.com/caddyserver/dist/blob/master/init/caddy.service)(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 -

```plaintext
$ 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.
