Installing Caddy
Download Caddy from the official website.
Give the executable root permissions1:
$ chown root ./caddy
$ chmod +s ./caddy
Caddyfile
Caddy reads it configuration for the Caddyfile. More information can be found in the official documentation.
Launchd config
Create a launchd plist file: ~/Library/LaunchAgents/com.caddyserver.web.plist. This file should look like this:
<?xml version=”1.0" encoding=”UTF-8"?>
<!DOCTYPE plist PUBLIC “-//Apple Computer//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version=”1.0">
<dict>
<key>Label</key>
<string>com.caddyserver.web</string>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/mathias</string>
</dict>
<key>ProgramArguments</key>
<array>
<string>sh</string>
<string>-c</string>
<string>launchctl unload -w /Applications/Server.app/Contents/ServerRoot/System/Library/LaunchDaemons/com.apple.serviceproxy.plist; ulimit -n 8192; cd /Users/mathias/Sites; ./caddy -agree -email my_email@domain.com -conf=/Users/mathias/Sites/Caddyfile</string>
</array>
<key>UserName</key>
<string>www</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>WorkingDirectory</key>
<string>/Users/mathias/Sites</string>
<key>StandardOutPath</key>
<string>/Users/mathias/Sites/caddy.log</string>
<key>StandardErrorPath</key>
<string>/Users/mathias/Sites/caddy_error.log</string>
</dict>
</plist>
The launchd script will stop the built-in Apache server on macOS X, set the ulimit and execute Caddy. The Caddy command line arguments are quite trivial:
-agree: agree to the CA’s (Let’s Encrypt) Subscriber Agreement.-email: your email address used for the certificates authority.-conf: full path to your Caddyfile.
The launchd file will make sure that Caddy is started at boot and kept alive.
Start and Stop Caddy launchd service using the following commands:
$ launchctl load ~/Library/LaunchAgents/com.caddyserver.web.plist
$ launchctl unload ~/Library/LaunchAgents/com.caddyserver.web.plist
- Yes, I know, it’s not good practice to run a service as root. But there is no other decent way on OS X.
setcapdoesn’t exist on macOS. Since Caddy won’t be touching any files, and Caddy doesn’t execute shell script, there is not a lot that can go wrong in this case. If you feel uncomfortable running Caddy as root, you could use portforwarding in your firewall and listen on a port >= 1024, for which you won’t need root privileges. This can be done using this command:echo " rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080 " | sudo pfctl -ef -
using your plist (with paths and such modified) I get “error presenting token: Could not start HTTPS server for challenge -> listen tcp :443: bind: permission denied”
launchd is running as root, so I don’t get why the permissions issue…
well… it seems random, because now it is complaining about port 80….
“Error presenting token: Could not start HTTP server for challenge -> listen tcp :80: bind: address already in use”
It seems there is already another service running on port 80. Maybe default Apache webserver?
P.s. I’m glad you pioneered getting this to work on mac server! Years ago, I used that bind to IP trick to put varnish in front of the server webservice (before everyone went https crazy). So I’m glad the same trick works now (server web proxy attaches to wildcard so the specific port binding trumps it out AND leaves that service available on localhost)
I might try putting varnish behind caddy, as that would give caching and a way to filter bad bots out … but I’m still playing with the ideas yet. Just wanted to see if I could get caddy running up front and your work helped me do it!
Have you done the first steps in the blogpost?
$ chown root ./caddy$ chmod +s ./caddy
looks like my earlier reply is gone. I got this to work by using a bind directive in each server block.
so the first line of each server block in the caddy file is bind xxx.xxx.xxx.xxx
the built in web proxy binds to the wildcard, so putting a service on the ip makes it the most specific and trumps the built-in server. the bonus is that if you leave webproxy running, you can still proxy it with caddy (I used to do this with varnish), and pass thru the built in web services.
So you’re running Caddy as a proxy before the default OS X server proxy?
That’s perfect! So to what IP do you bind caddy then? I didn’t knew the default OS X proxy ran on a different port…
yes. that’s a trick I learned years ago when I wanted to use varnish on mac server — the default web service binds to the wildcard — *:80 and *:443
that means you can bind a service to the external IP — so caddy has the bind command – you have to add it to the caddy file. so my caddy file looks like this now:
server.com {
bind xxx.xxx.xxx.xxx
….
}
I deleted the part about unloading the server proxy, and it’s still sitting there listening on *:80 and *:443. that means I can add a server block to proxy transparent to the host services, and calendar and such are still running and available as well.
if you had two or more external IPs, you could theoretically bind a different service to the same port on different IP addresses. So, filemaker server on port 443 of one IP, caddy on the port 443 of a different IP, and so on….
That’s great! Thanks for sharing this with me!
Because right now, I can indeed not use several service from the default webserver, but this would mean I could use the default macOS services AND have HTTPS on them.
Thanks!
awesome! I’m gald I could return a favor for your posts encouraging me to try caddy!
I’ve been using cloudflare as a cheat to get http/2 but I don’t really like it, and so I’ve been hunting for a way to get SSL and stuff to work on the mac, but I hated that the default webproxy is not http/2 anyway. You made this a good option!
Now, I am thinking that some of my tougher servers can just be served by the built-in apache (I have that optimized well) by proxying to them (I’m going to try this for mautic), and the wordpress sites and things can all be handled by caddy. Meanwhile, since my server services sit on one domain, I can just pass that entire domain over to the server web proxy and be done with those. The bonus is that all the apple authentication and stuff should still work as built — (I think I need to use the “Transparent” command in caddy file to make that work)… let me know if you get it working — I will be trying it all tomorrow as well
I don’t think I can test this soon. I messed up my macOS proxy so hard, I’m not able to launch it again…
So I’ll have to do a reinstall of macOS, which is more complicated since I’m running it on a hackintosh…
I got it and it works. here’s how to have caddy in front of the mac server services:
server-machine-domain.com {
bind xxx.xxx.xxx.xxx
proxy / https://127.0.0.1 {
insecure_skip_verify
transparent
}
}
Thanks! Why the
insecure_skip_verify?maybe I misunderstand how to apply that, but because the caddy and the server poxy are on the same machine, I figured it made no sense for the two of them to negotiate and validate TLS handshake. would just be extra time wasted on that, it seems.
Yeah, that makes sense…
But if you don’t run HTTPS on the default Apple proxy, it doesn’t matter.