Skip to content

Instantly share code, notes, and snippets.

@joepie91
Last active June 30, 2018 19:29
Show Gist options
  • Save joepie91/2b02afe2dfa65ef1d2b9975ca2d0ece3 to your computer and use it in GitHub Desktop.
Save joepie91/2b02afe2dfa65ef1d2b9975ca2d0ece3 to your computer and use it in GitHub Desktop.

What is Caddy?

Caddy is a modern open-source webserver, that is similar to Apache and nginx. However, it has a number of benefits compared to other webservers:

  • Caddy is a lot easier to configure.
  • Caddy automatically sets up TLS (also called "SSL"), without having to purchase an SSL certificate or needing to do complicated setup.
  • Caddy is safer - it's written in Go, which means it can't have memory-related security vulnerabilities.
  • Caddy supports modern features, like updating a site from a Git repository.

This article will explain:

  • How to install Caddy
  • How to make it work with PHP
  • How to make it work as a reverse proxy (for Node.js, Python, etc.)
  • How to support multiple domain names
  • How to set up rewrite rules
  • How to automatically update your site from a Git repository

This article assumes that you are logged into your VPS as root. If that is not the case, you may need to use sudo for some commands.

When should I not use Caddy?

While Caddy is great for most people, there are a few cases where you should use something else.

  • Caddy doesn't support .htaccess. If you need support for .htaccess, and you can't move the configuration rules to your Caddy configuration, you should use Apache instead.
  • Caddy is slightly slower than nginx (but still much faster than Apache). If you need extremely high throughput, you should probably use Nginx instead. If you're not sure, then Caddy will be fast enough.

Installing Caddy

Caddy is still a relatively new project, so most Linux distributions don't have it in their package repositories yet. This means that, for now, you still need to install it manually. Luckily, this isn't a lot of work.

This guide will assume that you already have a domain name, and that it is pointed at your VPS. If you don't have a domain name, you may run into errors later on.

No separate installation instructions for SSL/TLS certificates are needed. Unlike other webservers, Caddy will automatically set up SSL/TLS for free using Let's Encrypt, and you will not have to do any extra work to support this. It will Just Work.

Downloading Caddy

First of all, download Caddy from the Caddy download page. Select the features you want - don't forget to select git if you want to update your site from a Git repository - and then right-click the "Linux 64-bit" button. Then copy the download link - this URL will have been customized to include all the features you want.

Next, download it on your VPS. The exact URL will vary depending on what features you selected, but the download command will look something like this:

wget -O caddy.tar.gz 'https://caddyserver.com/download/build?os=linux&arch=amd64&features=git'

The apostrophes are important! Leaving them out will make Bash interpret the & characters as special characters. The -O caddy.tar.gz argument will store the resulting download as caddy.tar.gz.

Unpacking and installing

Next, we'll unpack the downloaded archive into a new folder, and navigate to that folder:

mkdir caddy
tar -xzvf caddy.tar.gz -C caddy/
cd caddy/

We don't need to compile Caddy - when you download it from the site, the archive already includes a compiled binary that can run on any (64-bits) Linux system.

To install Caddy, we simply copy the binary into /usr/local/bin, where other applications are installed as well, and set the right ownership and permissions:

cp caddy /usr/local/bin/caddy
chown root:root /usr/local/bin/caddy
chmod 755 /usr/local/bin/caddy

Enabling port 80/443

Normally, a process needs to be run as root to listen on port 80 (HTTP) and 443 (HTTPS). However, running software as root is a dangerous thing to do, so you should configure Caddy to have access to port 80 and 443 as a limited user, instead:

setcap 'cap_net_bind_service=+ep' /usr/local/bin/caddy

Testing the installation

To test whether Caddy was installed correctly, try running the following command:

caddy --help

If you get a long list of options, that means Caddy was installed successfully.

Creating a configuration file

Next, create a configuration file, known as the "Caddyfile". You'll modify this file later, depending on what you want to use Caddy for. In all the following instructions, you should replace example.com with your actual domain name.

First, create the directory that your configuration file will live in:

mkdir /etc/caddy

Then save the following as /etc/caddy/Caddyfile (changing the domain name to your own):

example.com {
    root /var/www/example.com
}

Don't forget to set the permissions on your new Caddyfile, and the directory that you've saved it to:

chown -R root:www-data /etc/caddy
chown www-data:www-data /etc/caddy/Caddyfile
chmod 444 /etc/caddy/Caddyfile

Creating the website root directory

We'll assume that the files for your site are in the /var/www/example.com folder - now we just need to create that folder and set the right permissions:

mkdir -p /var/www/example.com
chown -R www-data:www-data /var/www
chmod -R 555 /var/www

Note that this will make the folder read-only by default - if you are running a PHP application that requires the ability to write to disk, you may need to change the permissions for some folders.

Finally, store a testing page as /var/www/example.com/index.html:

<strong>Hello world!</strong>

Setting up a service

Because Caddy cannot be installed through a package manager yet, the archive comes with a few example service configurations for different Linux distributions, that you can install manually.

First of all, we need to make sure that the www-data user exists - this is the user account under which you will be running Caddy. Run the following command:

id www-data

You should see something like this, indicating that the user exists:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

If the user does not exist, you should create it along with the corresponding group:

groupadd -g 33 www-data
useradd \
  -g www-data --no-user-group \
  --home-dir /var/www --no-create-home \
  --shell /usr/sbin/nologin \
  --system --uid 33 www-data

Next, you need to determine what kind of service manager your Linux distribution uses. Simply go through the sections below, until you find one that works.

systemd

Run the following command:

systemctl --help

If this command produces usage instructions, you're running systemd, and you should follow the instructions below. If it says something along the lines of "command not found", skip this section.

Installing the service configuration

To install Caddy as a systemd service, you should first copy over the service configuration file to your services directory, and set the correct permissions and ownership:

cp init/linux-systemd/caddy.service /etc/systemd/system/
chown root:root /etc/systemd/system/caddy.service
chmod 744 /etc/systemd/system/caddy.service

Then load the new service configuration file:

systemctl daemon-reload

Creating the directories

After that, you should create the directory that Caddy needs to run as a service, and set its permissions and ownership:

mkdir /etc/ssl/caddy
chown -R www-data:root /etc/ssl/caddy
chmod 0770 /etc/ssl/caddy

Starting the service

Finally, you should start the new Caddy service, and configure it to automatically start when your VPS boots:

systemctl start caddy.service
systemctl enable caddy.service

Restarting the service

Sometimes, you'll need to restart the Caddy service - for example, after making certain kinds of configuration changes:

systemctl restart caddy.service

If you are running systemd, you should skip the sections for "upstart" and "sysvinit" below.

upstart

Run the following command:

initctl --help

If this command produces usage instructions, you're running upstart, and you should follow the instructions below. If it says something along the lines of "command not found", skip this section.

Installing the service configuration

To install Caddy as an upstart service, you should first copy over the service configuration file to your services directory, and set the correct permissions and ownership:

cp init/linux-upstart/caddy.conf /etc/init/caddy.conf
chown root:root /etc/init/caddy.conf
chmod 744 /etc/init/caddy.conf

Creating the directories

After that, you should create the directory that Caddy needs to run as a service, and set its permissions and ownership:

mkdir /etc/caddy/.caddy
chown -R www-data:www-data /etc/caddy/.caddy
chmod 0770 /etc/caddy/.caddy

Starting the service

Finally, you should start the new Caddy service:

service caddy start

Caddy will also automatically start after a reboot.

Restarting the service

Sometimes, you'll need to restart the Caddy service - for example, after making certain kinds of configuration changes:

service caddy restart

If you are running systemd, you should skip the section for "sysvinit" below.

sysvinit

Run the following command:

ls -al /etc/init.d/cron

If this command outputs information about a file, and both the systemd and upstart tests failed, you're running sysvinit and you should follow the instructions below. If it says something along the lines of "No such file or directory", and neither upstart nor systemd exist on your system, then you're using some other kind of init system - unfortunately, that also means that Caddy does not include an example service configuration for your distribution yet.

Installing the service configuration

To install Caddy as a sysvinit service, you should first copy over the service configuration file to your services directory, and set the correct permissions and ownership:

cp init/linux-sysvinit/caddy /etc/init.d/caddy
chown root:root /etc/init.d/caddy
chmod 744 /etc/init.d/caddy

Creating the directories

After that, you should create the directory that Caddy needs to run as a service, and set its permissions and ownership:

mkdir /etc/caddy/ssl
chown -R www-data:www-data /etc/caddy/ssl
chmod 0770 /etc/caddy/ssl

Starting the service

Finally, you should start the new Caddy service, and configure it to automatically start when your VPS boots:

service caddy start
update-rc.d caddy defaults

Restarting the service

Sometimes, you'll need to restart the Caddy service - for example, after making certain kinds of configuration changes:

service caddy restart

Testing the service

Now that you've successfully set up the Caddy service, you should verify that it's working correctly.

Try navigating to your domain name in a browser - if everything is set up correctly, you should see a page saying "Hello world!", and it will use SSL/TLS by default.

If you get an error instead, you should look up the documentation for the service manager on your system, and view the error logs for the Caddy service you just created. They should provide more details about what exactly went wrong.

Multiple domains

Caddy can serve more than one domain at the same time. To add another domain, add a configuration block and create a root directory for that domain. For example, your configuration might look something like this:

site-one.com {
    root /var/www/site-one.com
}

site-two.com {
    root /var/www/site-two.com
}

Then, to create a root directory for site-two.com, you would do something like this:

mkdir -p /var/www/site-two.com
chown www-data:www-data /var/www/site-two.com
chmod 555 /var/www/site-two.com

Simply restart the Caddy service, and the new domain will be available from the server as well.

Of course, you can have multiple subdomains as well:

site-one.com {
    root /var/www/site-one.com
}

history.site-one.com {
    root /var/www/history.site-two.com
}

And if you want to serve the same site on more than one domain, that's possible too:

site-one.com, site-one.net {
    root /var/www/site-one.com
}

Adding logging

You might also want to keep track of what pages people have visited. To do so, you'll want to enable access logs, using the log configuration option.

An example:

example.com {
    root /var/www/example.com
    log /var/log/access.log
}

You might also want to log errors, using the errors option:

example.com {
    root /var/www/example.com
    log /var/log/caddy-access.log
    errors /var/log/caddy-error.log
}

There are many more options for logging - they are described in detail in Caddy's access log documentation and error handling documentation.

Setting up reverse-proxying

Most languages, when used for web development, are their own webserver - these languages include Node.js, Python, and basically everything except PHP and ASP. To use Caddy for serving these kind of applications, you need to reverse-proxy to the application. This means that for every request that Caddy receives from a user, it forwards that request to your application - and subsequently, it will forward the response from the application to the user.

If you're only using PHP, then you can skip this section. Otherwise, read on.

Reverse-proxying is very easy to configure in Caddy - in the simplest cases, it's literally a single line of configuration:

example.com {
    proxy / localhost:3000
}

In this example, proxy tells Caddy to switch to reverse-proxying mode. The first argument tells it what "path prefix" to reverse-proxy for - in this case, that is /, which essentially means "the entire domain". The second argument tells it where to reverse-proxy to - in this case, that is localhost:3000, which means "port 3000 on this server".

Note how the root option is no longer there - because we're forwarding every request to an external application, we don't need to serve any files ourselves anymore. This also means we don't need a "root directory" for our domain.

Forwarding the user IP

A common requirement when reverse-proxying, is to pass along the original IP of the user. Because Caddy will be making requests on behalf of the user, it will look to your application like all requests are coming from 127.0.0.1. Thankfully, this is easy to fix with the transparent option:

example.com {
    proxy / localhost:3000 {
        transparent
    }
}

This instructs Caddy to send along Host, X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto headers containing information about the original request. These headers are the more-or-less standard headers that most applications expect to see, to support reverse-proxying.

Enabling WebSocket support

If you need to support WebSockets in your application, you'll also want to enable the websocket option:

example.com {
    proxy / localhost:3000 {
        transparent
        websocket
    }
}

These instructions only cover the simple cases for reverse proxying, and there are many more options available - for example, you can support multiple application processes, reverse-proxy to other servers, add custom headers, and much more. Full documentation of the proxy options in Caddy can be found in the Caddy documentation.

Setting up PHP with FastCGI

Unlike most other languages, PHP doesn't run as a webserver, and uses the FastCGI protocol instead. For PHP applications, you will typically use a tool named fpm to manage your application processes.

Installing PHP

Start by installing PHP with FPM support. The exact package name and command will vary by Linux distribution. Some examples:

  • Debian, Ubuntu: apt-get install php5-fpm
  • CentOS: yum install php-fpm php-common or dnf install php-fpm php-common

This guide won't go into detailed PHP-FPM installation and configuration instructions, but in most cases it will work out of the box.

Finding the FPM socket

Different Linux distributions have different default configurations for FPM. Below are the two most common configurations:

Unix domain socket

Run the following command:

ls -al /var/run/php5-fpm.sock

If this outputs file information, your FPM installation is running on a Unix domain socket. In the configuration described below, you should specify your FastCGI endpoint as /var/run/php5-fpm.sock.

TCP port 9000

Run the following command:

lsof -i -n -P | grep ":9000 (LISTEN)"

If this produces output that mentions fpm somewhere, your FPM installation is running on TCP port 9000. In the configuration described below, you should specify your FastCGI endpoint as 127.0.0.1:9000.

Other configurations

If neither of the above commands worked for you, your Linux distribution has a different configuration. You should consult a tutorial for your distribution to find out how to configure FPM, and what its endpoint is.

Configuring Caddy for FPM

Now that you know the FPM endpoint, the only thing you need to do is adding it to your Caddy configuration. The format is as follows:

example.com {
    root /var/www/example.com
    fastcgi / <endpoint> php
}

For example, if your FPM endpoint is /var/run/php5-fpm.sock, your configuration would look like this:

example.com {
    root /var/www/example.com
    fastcgi / /var/run/php5-fpm.sock php
}

Excluding upload directories

If you are running something that allows uploads from users (for example, for avatar images), then you'll want to make sure that files in the upload directory can't be executed as PHP. Otherwise, malicious users could upload PHP files and compromise your server.

In Caddy, this takes only one line of configuration to do. For example, assuming that your upload directory is at /var/www/example.com/uploads, this is what your configuration would look like:

example.com {
    root /var/www/example.com
    fastcgi / /var/run/php5-fpm.sock php {
        except uploads/
    }
}

That's all! Restart Caddy, and your PHP application should now be working. If you need to configure more advanced options for FastCGI, have a look at the Caddy documentation.

Rewrite rules

Caddy also supports rewrite rules, much like other webservers do. Rewrite rules allow you internally 'forward' one URL (pattern) to another, but without changing the URL that the user sees. It's commonly used for PHP applications, to support "clean URLs".

While the exact rewrite patterns to use will depend on the application you're running, some examples of common patterns are demonstrated below. Keep in mind that you can specify multiple rewrite rules as well - simply specify each one on a new line.

Rewriting exact paths

If you just want to rewrite one path to another, you can use the following format:

example.com {
    root /var/www/example.com
    fastcgi / /var/run/php5-fpm.sock php

    rewrite <source> <destination>
}

For example, to rewrite /foo to /bar.php, your configuration would look like this:

example.com {
    root /var/www/example.com
    fastcgi / /var/run/php5-fpm.sock php

    rewrite /foo /bar.php
}

Using a PHP-based router

Many modern PHP frameworks and applications have their own, internal router for handling requests. For these kind of applications, you need to rewrite every non-existent path to a single PHP file. In other webservers, this is known as a FallbackResource directive (Apache), try_files (nginx), or server.error-handler-404 (lighttpd).

In Caddy, this is accomplished as follows:

example.com {
    root /var/www/example.com
    fastcgi / /var/run/php5-fpm.sock php

    rewrite {
        to {path} {path}/ /router.php?{query}
    }
}

The above configuration will, for every incoming request, try the following:

  1. Access the original request path as a file using {path}.
  2. Access the original request path as a folder using {path}/.
  3. Give up, and forward it to /router.php in your website root, no matter what the original request path is, but passing on the original query arguments.

In practice, this means that static files and directory listings are served as normal, but that anything else is handled by your PHP application.

The example above assumes that the "entry point" of your PHP application is /router.php - if not, you will have to change the filename. For example, for WordPress, this would be /index.php.

Rewriting with regular expressions

Caddy also supports rewrite rules that use regular expressions, for more complex cases. However, keep in mind that like in other webservers, regular expressions are slow. For most sites this won't matter, but if you need to handle a large amount of requests, you'll want to avoid regular expressions in your rewrite rules.

Nevertheless, the format is as follows:

example.com {
    root /var/www/example.com
    fastcgi / /var/run/php5-fpm.sock php

    rewrite /app {
        r  page_(.*)
        to /page.php?path={1}
    }
}

This would rewrite any request that starts with page_, to /page.php?path= followed by the rest of the path - {1} is a reference to the first matching group in the expression. For example, page_about would be rewritten to /page.php?path=about.

There are many more advanced options for rewrite rules, and you can find them in the Caddy documentation.

Updating your site from a Git repository

To use this feature, you must select the git middleware when downloading Caddy.

Caddy also has support for Git. What this means in practice: if you have a Git repository containing the code for your site, Caddy can automatically update the code on the server whenever the repository updates. Note that this only works for static and PHP sites - when using Caddy for reverse proxying, Caddy doesn't control the application, and so it can't update it either.

Before using this feature, you should make sure that Git is installed on your VPS. The exact install instructions vary between Linux distributions, but generally you need to install a package named git or git-core. You don't need a GitHub account, unless that's where you want to host your repository - this feature works with any Git repository, hosted anywhere.

To enable Git support in Caddy, you simply add a git configuration option, like so:

example.com {
    root /var/www/example.com
    git https://github.com/your-username/your-repository.git
}

For a PHP site, it works the same, and it may look something like this:

example.com {
    root /var/www/example.com
    fastcgi / /var/run/php5-fpm.sock php
    git https://github.com/your-username/your-repository.git
}

Updating from a subdirectory in your repository

Sometimes, your actual site is found in a subdirectory of the repository - an example of this is a Jekyll-generated static site, where the generated site can be found in the _site directory.

Caddy doesn't (yet) explicitly support this usecase, so you need to use a workaround:

example.com {
    root /var/www/example.com/_site
    git https://github.com/your-username/your-repository.git ..
}

Note the change to the root option, as well as the added .. in the git option. We're telling Caddy that the website is located in /var/www/example.com/_site, but that it should be cloned to .. relative to the root (ie. the parent directory, which is /var/www/example.com).

More advanced options, such as webhooks or changing the update interval, can be found in the Caddy documentation.

Conclusion

Caddy is an easy-to-configure and secure webserver, with a lot of features.

This article only covered some of the most common setups, and there are many more options and features available - you can find full documentation on all of these features in the Caddy User Guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment