在 macOS 上使用 Cyberduck 作为 FTP 或 SFTP 客户端上传静态网站很常见。问题是,当本地 CSS 文件已经更新,远程目录也显示覆盖成功,浏览器刷新后却仍然沿用旧样式。弄清楚这个现象,需要同时关注上传操作和缓存机制。Cyberduck 本身不会主动通知浏览器刷新资源,但可以通过一些配置和引用策略,让修改尽可能即时呈现。

Cyberduck 一键上传的正确姿势
Cyberduck 支持 FTP、SFTP、WebDAV、Amazon S3 等多种协议,macOS 版本免费。将常用连接保存为书签后,双击即可进入远程目录,不必每次输入主机地址和账号。为了减少重复操作,可以先打开 Cyberduck 的偏好设置,在浏览器中启用双击编辑功能,这样双击远程文件会用本地关联的编辑器打开,保存后可直接回传。但更高效的是本地目录和远程目录的同步功能。右键远程目录选择同步,再选择本地文件夹,Cyberduck 会比较两边文件的时间戳和大小,只上传发生变化的文件,这就是一键上传的基础。
如果没有配置同步,也可以把常用的远程目录和本地目录都放进访达侧边栏,再次上传时只需选中本地 CSS 文件拖入 Cyberduck 窗口,选择覆盖即可。但拖拽方式容易把文件传到错误层级,比如远程 css/ 目录下又生成一个 css/style.css。同步功能会按相对路径对齐,因此更适合频繁更新样式。同步前务必核对远程根目录,看服务器账号的默认登录目录是站点根目录还是上一级目录。很多主机商的 FTP 登录后会落在 / 或 /html 目录,如果本地根目录是 mysite,同步目标必须选 /var/www/mysite 而不是 /var/www。
CSS 更新后浏览器不刷新的原因
浏览器请求外链 CSS 时会根据响应头决定是否缓存。服务器返回强缓存字段如 Cache-Control: max-age=31536000,一年内浏览器不会再向服务器确认,直接读本地缓存。即使 Cyberduck 已经上传了新文件,浏览器没有发起请求,自然不会知道内容变了。另一种协商缓存依赖 ETag 或 Last-Modified,浏览器会带条件请求,服务器返回 304 后仍然使用本地缓存。如果 CDN 配置了较长的缓存时间,边缘节点也可能继续提供旧文件。
除了缓存,还要检查文件是否真的被上传到正确地址。可以用浏览器开发者工具的网络面板查看 style.css 响应头中的 Server、Last-Modified 或 ETag,再与 Cyberduck 远程目录中的修改时间对比。也有一种情况是 HTML 页面引用的 CSS 路径与实际上传路径不一致,例如页面里写 href="css/style.css",而本地编辑的 style.css 被上传到根目录。此时页面找不到新文件,可能直接报 404 或继续使用上一次成功的缓存版本。把这些基础问题排除后,再从引用策略下手。
让 CSS 修改即时生效的实用方法
最简单的方法是在 HTML 中给 CSS 地址追加版本号,每次修改后递增。浏览器会把带不同查询参数的地址视为不同资源,强制获取新文件。写法如下:
<link rel="stylesheet" href="css/style.css?v=1.0.2">
版本查询参数不会改变服务器文件本身,但会绕过强缓存。修改 <link rel="stylesheet" href="css/style.css?v=1.0.2"> 中的 v 值即可。它的缺点是每次更新都要手动修改 HTML,适合页面数量较少、CSS 文件也不多的小型静态站。如果 CSS 由构建工具生成,可以使用文件名哈希。比如把 style.css 重命名为 style.a1b2c3.css,HTML 引用同步修改。构建完成后通过 Cyberduck 同步上传所有文件。文件名哈希天然保证内容变化时 URL 变化,未变化的文件仍可长期缓存。
对 HTML 页面设置 no-cache 或较短的 max-age,确保入口文件总是校验;CSS 文件则可以继续使用长缓存,因为 URL 中的版本参数或哈希已经在内容变化后改变地址。以 Apache 为例,可以在 .htaccess 中按文件类型设置响应头:
<IfModule mod_expires.c> ExpiresActive On ExpiresByType text/html "access plus 0 seconds" ExpiresByType text/css "access plus 1 year" </IfModule>
Nginx 配置类似:
location ~* \.css$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache";
}
这样 HTML 不缓存,CSS 长缓存。用户每次改 CSS 后更新 HTML 中的版本号,再上传两个文件即可。在浏览器中按 Command+Shift+R 可以强制刷新页面,忽略缓存重新请求所有资源。开发者工具打开时勾选 Network 面板的 Disable cache 也能避免缓存干扰。但这些只是本机调试手段,不能解决所有访客的缓存问题,所以线上应以版本参数或哈希为主要方案。
使用 duck 命令行把上传自动化
Cyberduck 自带命令行工具 duck,适合把上传写进脚本。安装后可以用一条命令覆盖远端 CSS:
duck --upload ftp://user@yourserver.com/htdocs/css/ ./css/style.css --existing overwrite
如果需要和 HTML 一起发布,可以先修改版本号,再执行同步:
duck --upload ftp://user@yourserver.com/htdocs/ ./public/ --existing overwrite --parallel 4
参数 --existing overwrite 表示覆盖已存在文件,--parallel 4 提升并发。更完整的发布脚本可以用 Shell 实现,将版本号作为变量写入 HTML,再调用 duck 上传。这样在 Mac 上双击脚本或者通过 Automator 绑定快捷键,就能完成一键上传和版本更新,做到 CSS 修改后 HTML 站即现。