Dong Nguyen
caddy

Caddy Docs

Install

export CADDY_VERSION=2.9.1
wget https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/caddy_${CADDY_VERSION}_linux_amd64.tar.gz
tar zxf caddy_${CADDY_VERSION}_linux_amd64.tar.gz

# arm
wget https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/caddy_${CADDY_VERSION}_linux_arm64.tar.gz
tar zxf caddy_${CADDY_VERSION}_linux_arm64.tar.gz

# check
./caddy version
# move to bin
sudo mv ./caddy /usr/local/bin/
# check again
caddy version

# clean unneccessary file
rm caddy_${CADDY_VERSION}_linux_amd64.tar.gz
rm caddy_${CADDY_VERSION}_linux_arm64.tar.gz

Createa Caddyfile

sudo mkdir -p /etc/caddy/sites
sudo vim /etc/caddy/Caddyfile

Caddyfile

{
  # debug
  email admin@pwshub.com
  
  servers :443 {
    protocols h1 h2c h3
  }
}

:80 {
    root * /workspace/html/default
    file_server
    encode gzip
    header server "PrivEngine/2.1"
    header X-Forwarded-For $proxy_add_x_forwarded_for
    header Host $host
    header Upgrade $http_upgrade
    header Connection "upgrade"
  
    header {
        Strict-Transport-Security max-age=31536000;
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        Referrer-Policy no-referrer-when-downgrade
    }

    handle_errors {
      respond "{err.status_code} {err.status_text}"
    }

    log {
      format json
      output file /workspace/runtimes/logs/caddy/default/access.log {
        roll_size 10mb
        roll_keep 7
        roll_keep_for 168h
      }
    }
}

import sites/*.caddy

Create service

sudo vim /etc/systemd/system/my-caddy.service

caddy.service

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=ndaidong
Group=ndaidong
ExecStart=/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
#LimitNOFILE=1048576
#LimitNPROC=512
#PrivateTmp=true
#ProtectSystem=full	
AmbientCapabilities=CAP_NET_BIND_SERVICE
#RuntimeDirectory=caddy

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable my-caddy.service
sudo systemctl start my-caddy.service
sudo systemctl status my-caddy.service

Use docker

mkdir /opt/engines/caddy
cd /opt/engines/caddy
vim docker-compose.yaml

docker-compose.yaml

services:
  caddy:
    image: caddy:2.9.1-alpine
    container_name: caddy
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./storage/Caddyfile:/etc/caddy/Caddyfile
      - ./storage/sites:/etc/caddy/sites
      - ./storage/logs:/workspace/runtimes/logs
      - ./storage/html:/workspace/html/default
      - ./storage/data:/data
      - ./storage/config:/config
    restart: unless-stopped

Ensure there is index.html and favicon.ico within ./site as they will be loaded as default site.

Reload config:

docker-compose exec -w /etc/caddy caddy caddy reload

View logs:

docker-compose logs caddy -f

Reverse proxy optimization

:80 {
    tls {
        protocols tls1.2 tls1.3
    }
    
    # Set up timeouts
    timeouts {
        read 30s     # Time to allow for reading the request
        write 30s    # Time to allow for writing the response
        idle 5m      # Time to allow for an idle connection
    }
    
    # Limit the number of simultaneous connections
    limits {
        max_conns_per_ip 10
        max_reuse 25
    }
    
    # Caddyfile snippet demonstrating HTTP caching headers
    @cacheable {
        path *.css *.js *.jpg *.jpeg *.png *.gif *.webp *.svg
        not header Cache-Control *no-cache*
    }
    header @cacheable Cache-Control "max-age=5184000"
        
    # Caddyfile snippet with reverse proxy cache
    reverse_proxy / localhost:9000 {
        header_up Host {host}
        header_up X-Real-IP {remote}
        header_up X-Forwarded-For {remote}
        header_up X-Forwarded-Port {server_port}
        header_up X-Forwarded-Proto {scheme}
        cache {
            ttl 30m    # Time to live for cached content
            max_size 300MB  # Maximum size of the cache
        }
        
        to server1:9000 server2:9000
        lb_policy least_conn        
    }
    cache {
        match_path /public/*
        default_max_age 30m
    }
}

Basic Auth

Generate crypted password

caddy hash-password -p PASSWORD -a ALGORITHM

Available algorithm: bcrypt → this is default

Example:

caddy hash-password -p YOUR_PASS

Set basic auth

example.com {
  basic_auth {
	ndaidong GENERATED_HASH
  }
  respond "Welcome, {http.auth.user.id}" 200
}

Protect files in /secret/ so only ndaidong can access them (and anyone can see other paths):

example.com {
  root * /srv
  basic_auth /secret/* {
	ndaidong GENERATED_HASH
  }
  file_server
}

Caddy for local dev

Setup HTTPS for local server

Install caddy to get caddy command, and:

sudo apt install libnss3-tools

Configure host file

sudo vim /etc/hosts

// add a new line
127.0.0.1	mydomain.local

Use command line

sudo caddy reverse-proxy --from mydomain.local --to :8000

Use config file

{
  debug
}

mydomain.local {
    reverse_proxy 0.0.0.0:8000
    encode gzip
    header server "PrivEngine/2.0"
    header X-Forwarded-For $proxy_add_x_forwarded_for
    header Host $host
}
sudo caddy run --config ./Caddyfile 

Reverse proxy with Rewrite

Source

https://extractus.local/feed/parse?query

Target

http://localhost:8001/extract?query

Here we forward a request from extractus.local/feed/parse to localhost:8001/extract, different host and path

extractus.local {
    rewrite /feed/parse /extract

    @feed {
        method GET
        path /extract
    }
    
    reverse_proxy @feed 0.0.0.0:8001
    encode gzip
    header server "PrivEngine/2.0"
    header X-Forwarded-For $proxy_add_x_forwarded_for
    header Host $host
}

GoAccess log

static

cat /workspace/runtimes/logs/caddy/thucchien-dev/access.log | docker run --rm -i -e LANG=$LANG allinurl/goaccess -a -o html --log-format=CADDY - > report.html

realtime

cat ./access.log | docker run --rm -i -e LANG=$LANG allinurl/goaccess -a -o html --log-format=CADDY  --real-time-html - > /workspace/html/default/report.html