Skip to content

Logs

Every request handled by the webserver is logged for you, so you can debug issues and see who accessed your site.

Access logs

You'll find your webserver's access log at ~/logs/caddy, which resolves to /var/log/caddy-userlogs/$USER.

[isabell@moondust ~]$ tail --follow=name ~/logs/caddy/access.log

{"level":"info","ts":"2026-07-11T11:02:18.428+0200","logger":"http.log.access.[...]","msg":"handled request","request":{"client_ip":"192.0.2.100","proto":"HTTP/2.0","method":"GET","host":"isabell.uber.space","uri":"/robots.txt","headers":{"User-Agent":["[...]"]}},"bytes_read":0,"user_id":"","duration":0.002604887,"size":512,"status":200}

Always on

There is currently no way to disable this log.

Each line is a JSON object. The most relevant fields are:

{
  "ts": "2026-07-11T11:02:18.428+0200",
  "request": {
    "client_ip": "192.0.2.100",
    "proto": "HTTP/2.0",
    "method": "GET",
    "host": "isabell.uber.space",
    "uri": "/robots.txt"
  },
  "status": 200,
  "size": 512,
  "duration": 0.002604887
}

You can use jq to filter and format the log, for example to only show the timestamp, method, path and status code:

[isabell@moondust ~]$ tail --follow=name ~/logs/caddy/access.log | jq --unbuffered --raw-output '[.ts, .request.method, .request.uri, .status] | @tsv'

2026-07-11T11:02:18.428+0200    GET    /robots.txt    200

Apache-style logs

Some tools expect the Apache combined log format. You can use jq to convert the live output:

[isabell@moondust ~]$ tail --follow=name ~/logs/caddy/access.log | jq --unbuffered --raw-output '
def apache_time:
  capture("^(?<date>[0-9-]+T[0-9:]+)(?:\\.[0-9]+)?(?<zone>[+-][0-9]{4})$")
  | ((.date + "Z" | fromdateiso8601 | strftime("%d/%b/%Y:%H:%M:%S")) + " " + .zone);
[
  .request.client_ip,
  "-",
  ((.user_id | select(length > 0)) // "-"),
  ("[" + (.ts | apache_time) + "]"),
  ("\"" + .request.method + " " + .request.uri + " " + .request.proto + "\""),
  (.status | tostring),
  (.size | if . == 0 then "-" else tostring end),
  ("\"" + (.request.headers.Referer[0] // "-") + "\""),
  ("\"" + (.request.headers["User-Agent"][0] // "-") + "\"")
]
| join(" ")'

192.0.2.100 - - [11/Jul/2026:11:02:18 +0200] "GET /robots.txt HTTP/2.0" 200 512 "-" "[...]"

This only reformats the command's output. The original log remains unchanged in structured JSON and can still be filtered and parsed directly.