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.
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.
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.
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
.
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
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
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.
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
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>
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.
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.
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
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
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
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.
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.
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
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
Finally, you should start the new Caddy service:
service caddy start
Caddy will also automatically start after a reboot.
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.
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.
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
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
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
Sometimes, you'll need to restart the Caddy service - for example, after making certain kinds of configuration changes:
service caddy restart
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.
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
}
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.
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.
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.
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.
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.
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
ordnf 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.
Different Linux distributions have different default configurations for FPM. Below are the two most common configurations:
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
.
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
.
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.
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
}
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.
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.
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
}
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:
- Access the original request path as a file using
{path}
. - Access the original request path as a folder using
{path}/
. - 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
.
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.
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
}
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.
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.