CalDAV(Calendaring Extensions to WebDAV)是 IETF 在 RFC 4791 中定义的开放标准,本质是在 WebDAV/HTTP 之上给“日历数据”加的一套扩展语义。它让任意兼容客户端(Apple 日历、Thunderbird、DAVx5、GNOME Calendar 等)能以统一方式读写远端服务器上的日程,实现跨厂商、跨设备的双向同步与共享。
一、它处在协议栈的哪一层
- HTTP/HTTPS:传输层,所有请求都是标准 HTTP 方法 + 状态码
- WebDAV(RFC 4918):把“远端目录/文件”抽象成“集合/资源”,提供
PROPFIND、PROPPATCH、MKCOL、DELETE、属性、锁、ACL 等 - CalDAV(RFC 4791):在 WebDAV 集合模型上规定——哪些集合是“日历集合”、里面子资源必须是 iCalendar 对象、新增
MKCALENDAR、REPORT里的calendar-query/multiget/free-busy-query等 - iCalendar(RFC 5545):真正装数据的格式,
VEVENT/VTODO/VJOURNAL/VTIMEZONE写成.ics文本 - 可选扩展:RFC 6638(调度/邀请)、RFC 6578(sync-token 增量同步)、RFC 7809(时区预处理)
简单说:WebDAV 管“怎么像操作文件一样操作资源”,CalDAV 管“这些资源被解释为日历是什么意思”,iCalendar 管“事件本身长啥样”。
二、数据模型:principal → home → calendar collection → object
https://cal.example.com/
└── principals/users/alice/ current-user-principal
└── calendars/alice/ CALDAV:calendar-home-set
├── work/ calendar collection (resourcetype=calendar)
│ ├── event-1.ics calendar object resource (VEVENT)
│ └── event-2.ics
└── personal/ another calendar collection
- Principal:代表“用户/身份”,发现入口。
PROPFIND先问current-user-principal - Calendar Home Set:该用户拥有的所有日历集合的根,属性
CALDAV:calendar-home-set - Calendar Collection:一种特殊 WebDAV 集合,
resourcetype含C:calendar,带supported-calendar-component-set(允许 VEVENT/VTODO…)、显示名、颜色、CTag 等 - Calendar Object Resource:集合下的子资源,URL 通常以
.ics结尾,主体是单个VCALENDAR文本,带ETag
三、核心 HTTP 方法与典型交互
CalDAV 没有发明新传输,只是复用/扩展 HTTP 动词:
| 方法 | 作用 | 典型场景 |
|---|---|---|
OPTIONS | 探能力 | 客户端首连看服务器是否声明 CalDAV 支持 |
PROPFIND | 读属性/发现结构 | 查 current-user-principal、calendar-home-set、列出日历集合、getetag/displayname |
PROPPATCH | 改集合属性 | 改日历名、改颜色、改描述 |
MKCALENDAR | 建日历集合 | 新建“工作日历”(Google 不支持,Nextcloud/Radicale 支持) |
GET | 取单个 .ics 原文 | 拉某个事件完整 iCalendar |
PUT | 新建/覆盖事件 | 建事件带 If-None-Match: *;改事件带 If-Match: <旧ETag> 做并发保护 |
DELETE | 删事件/集合 | 删单个 VEVENT 资源 |
REPORT | CalDAV 灵魂 | calendar-query(按时间窗/组件类型过滤)、calendar-multiget(按 href 批量取)、sync-collection(增量) |
响应大量使用 207 Multi-Status + DAV:multistatus XML 包装多个资源的属性/数据/错误。
一个最小建事件示例:
PUT /calendars/alice/work/meeting-123.ics HTTP/1.1
Host: cal.example.com
Content-Type: text/calendar
If-None-Match: *
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example//CalDAV//EN
BEGIN:VEVENT
UID:meeting-123@example.com
DTSTAMP:20260831T120000Z
DTSTART:20260901T010000Z
DTEND:20260901T020000Z
SUMMARY:Sync design review
END:VEVENT
END:VCALENDAR
成功回 201 Created 带新 ETag。
四、同步机制(重点)
CalDAV 同步不是“每次全量下载”,而是三层游标:
- ETag(单事件级)
每个.ics资源有ETag。客户端缓存href + ETag,下次PROPFIND Depth:1只拿(href, getetag),比对就能知道哪个事件被改/删。 - CTag / getctag(日历集合级)
集合整体变更计数。没变就跳过,变了再进集合扫 ETag。 - sync-token(RFC 6578 增量)
客户端存上次sync-token,发REPORT DAV:sync-collection带旧 token,服务器只回“自那以后新增/修改/删除的 href 列表”,带宽最小。这是现代客户端(iOS、macOS、DAVx5)的主流模式。
时间窗查询也很关键:客户端打开“本月视图”时发
REPORT /calendars/alice/work/
<calendar-query><prop><getetag/></prop>
<filter><comp-filter name="VCALENDAR"><comp-filter name="VEVENT">
<time-range start="20260901T000000Z" end="20261001T000000Z"/>
</comp-filter></comp-filter></filter>
</calendar-query>
服务器只回落在窗口内的事件,避免拉全年历史。
冲突处理靠 If-Match + ETag:两人同时改同一事件,后写者若带旧 ETag 会被 412 Precondition Failed 拒绝,客户端再决定覆盖还是合并。
五、发现(Service Discovery)
只填一个服务器域名就能用,靠两套机制:
.well-known/caldav(RFC 5785):客户端先试https://host/.well-known/caldav拿到真实根路径- Principal 链:
PROPFIND根 →current-user-principal→ 其上的calendar-home-set→PROPFINDhome 集合Depth:1列出各日历
Nextcloud 的入口通常是https://host/remote.php/dav,Radicale 默认https://host:5232/,iOS/macOS 添加“其他 CalDAV 账户”走的就是这条链。
六、安全与认证
- 传输:必须 HTTPS(iOS 12+ 强制 TLS,自签证书需信任)
- 认证:HTTP Basic(最普遍,配合应用专用密码)、Digest、Bearer/OAuth2(Google、Fastmail、部分企业部署)
- 授权:WebDAV ACL(RFC 3744)控制到集合/资源级——可读、可写、可共享给别人
- 服务端可对每个日历集合单独设 ACL,所以“共享给同事只读”是原生能力
七、CalDAV vs CardDAV vs 私有协议
- CardDAV(RFC 6352):亲兄弟,把同一套 WebDAV 机制用在通讯录上,数据是 vCard 不是 iCalendar。两者常成对出现(Nextcloud、Radicale、iCloud 都同时开)
- iCalendar 订阅(.ics 链接):只读广播,无 PUT/DELETE,不是 CalDAV
- Exchange EWS/ActiveSync:微软私有体系,Outlook 原生走这个;连非 Exchange 日历才需要 CalDAV 插件
- Google Calendar API:Google 同时提供私有 GData API 和受限 CalDAV 接口(不支持 MKCALENDAR、VTODO、忙闲查询)
八、常见服务端 / 客户端
- 服务端:Nextcloud、Radicale(轻量 Python)、Baïkal、DAViCal、SOGo、Fastmail、iCloud、Google(部分)
- 客户端:Apple 日历(macOS/iOS 原生)、Thunderbird+Lightning、GNOME Calendar/Evolution、Outlook(需插件)、Android 上 DAVx5 桥接系统日历
- 自托管最小闭环:
Radicale+ nginx 反代 TLS + DAVx5/iOS 原生 = 完全脱离厂商的日程同步
九、实现一个最小 CalDAV 服务器要答哪些请求
按 RFC 4791 的“MUST”:
OPTIONS在日历资源上返回Allow含PROPFIND REPORT PUT DELETE等,且DAV头含1和calendar-accessPROPFIND能答resourcetype、displayname、current-user-principal、calendar-home-set、supported-calendar-component-set、getetagPUT校验 Content-Typetext/calendar、解析 iCalendar、按 UID 落库、发 ETagREPORT支持calendar-query(至少按时间窗过滤 VEVENT)、calendar-multigetDELETE删资源;可选MKCALENDAR、sync-collection、ACL- 错误用
207内嵌DAV:error或403/409带 CalDAV 预条件 XML 元素