惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
AWS News Blog
AWS News Blog
Last Week in AI
Last Week in AI
宝玉的分享
宝玉的分享
The Cloudflare Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 三生石上(FineUI控件)
腾讯CDC
Apple Machine Learning Research
Apple Machine Learning Research
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
人人都是产品经理
人人都是产品经理
WordPress大学
WordPress大学
爱范儿
爱范儿
IT之家
IT之家
博客园 - 叶小钗
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题
L
Lohrmann on Cybersecurity
Scott Helme
Scott Helme
G
GRAHAM CLULEY
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Visual Studio Blog
博客园 - 聂微东
有赞技术团队
有赞技术团队
J
Java Code Geeks
小众软件
小众软件
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
V2EX
The Last Watchdog
The Last Watchdog
S
Securelist
博客园 - Franky
罗磊的独立博客
阮一峰的网络日志
阮一峰的网络日志
AI
AI
雷峰网
雷峰网
博客园 - 司徒正美
L
LINUX DO - 热门话题
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
Hugging Face - Blog
Hugging Face - Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LINUX DO - 最新话题
TaoSecurity Blog
TaoSecurity Blog
Cisco Talos Blog
Cisco Talos Blog
K
Kaspersky official blog
H
Hacker News: Front Page
量子位
Latest news
Latest news

alexwlchan

Preventing line breaks in <code> elements Fixing a bug with byte order marks A Git hook to prevent committing directly to main Describing all my photos I don’t want to repeat repeat myself Rebuilding the computer room What can wonky APIs tell us about the web? Using the Screen Capture API to record a browser window Using Pytester to test my Playwright fixtures Rendering a chat thread in CSS and JavaScript Waiting for website changes in the browser Watching for file changes on macOS Using Playwright to test my static sites Building a basic cache with SQLite HTTP GET requests with the Python standard library Auditing my local Python packages Quietly quantum-resistant blogging Creating a personalised bin calendar Monki Gras 2026 “Prepping Craft” The selfish case for public libraries Dreaming of a ten-year computer Gumdrop, a silly app for messing with my webcam The bare minimum for syncing Git repos Swapping gems for tiles Parody posters for made-up movies The Good, the Bad, and the Gutters Using perceptual distance to create better headers The passwords I actually memorise Where I store my multi-factor recovery codes Quick-and-dirty print debugging in Go My favourite books from 2025 Drawing Truchet tiles in SVG Adding a README to S3 buckets with Terraform The palm tree that led to Palmyra
Creating Caddyfiles with Cog
2026-02-05 · via alexwlchan

I’m currently restructuring my site, and I’m going to change some of the URLs. I don’t want to break inbound links to the old URLs, so I’m creating redirects between old and new.

My current web server is Caddy, so I define redirects in my Caddyfile with the redir directive. Here’s an example that creates permanent redirects for three URLs:

alexwlchan.net {
  redir /videos/crossness_flywheel.mp4  /files/2017/crossness_flywheel.mp4 permanent
  redir /2021/12/2021-in-reading/       /2021/2021-in-reading/ permanent
  redir /2022/12/print-sbt/             /til/2022/print-sbt/ permanent
}

This syntax is easy to write by hand, but it’s annoying if I want to define lots of redirects – and when I’m doing a big restructure, I do. In particular, it’s tricky to write scripts to modify this file.

This is a good use case for Cog, made by Ned Batchelder.

How I automate this with Cog

Cog is a tool for running snippets of Python inside text files, allowing you to generate content without external templates or additional files. When you process a file with Cog, it finds those snippets of Python, executes them, then inserts the output back into the original file.

Here’s an example:

alexwlchan.net {
  #[[[cog
  # import cog
  # 
  # redirects = [
  #     {"old_url": "/videos/crossness_flywheel.mp4", "new_url": "/files/2017/crossness_flywheel.mp4"},
  #     {"old_url": "/2021/12/2021-in-reading/", "new_url": "/2021/2021-in-reading/"},
  #     {"old_url": "/2022/12/print-sbt/", "new_url": "/til/2022/print-sbt/"},
  # ]
  # 
  # for r in redirects:
  #     cog.outl(f"redir {r['old_url']} {r['new_url']} permanent")
  #]]]
  #[[[end]]]
}

All the Python code that Cog runs is inside a comment, so it will be ignored by Caddy. The [[[cog …]]] and [[[end]]] markers tell Cog where to find the code, and it’s smart enough to remove the leading whitespace and comment markers.

When I process this file with Cog (pip install cogapp; cog Caddyfile), it runs the Python snippet, and anything passed to cog.outl() is written between the markers. This is the output, which gets printed to stdout:

alexwlchan.net {
  #[[[cog
  # import cog
  # 
  # redirects = [
  #     {"old_url": "/videos/crossness_flywheel.mp4", "new_url": "/files/2017/crossness_flywheel.mp4"},
  #     {"old_url": "/2021/12/2021-in-reading/", "new_url": "/2021/2021-in-reading/"},
  #     {"old_url": "/2022/12/print-sbt/", "new_url": "/til/2022/print-sbt/"},
  # ]
  # 
  # for r in redirects:
  #     cog.outl(f"redir {r['old_url']} {r['new_url']} permanent")
  #]]]
  redir /videos/crossness_flywheel.mp4 /files/2017/crossness_flywheel.mp4 permanent
  redir /2021/12/2021-in-reading/ /2021/2021-in-reading/ permanent
  redir /2022/12/print-sbt/ /til/2022/print-sbt/ permanent
  #[[[end]]]
}

If I want to write the output back to the file, I run Cog with the -r flag (cog -r Caddyfile). All the original Cog code is preserved, so I can run it again and again to regenerate the file. This means that if I want to add a new redirect, I can edit the list and run Cog again.

Cog is running a full version of Python, so I can rewrite the snippet to read the list of redirects from an external file. Here’s another example:

alexwlchan.net {
  #[[[cog
  # import cog
  # import json
  #
  # with open("redirects.json") as in_file:
  #     redirects = json.load(in_file)
  # 
  # for r in redirects:
  #     cog.outl(f"redir {r['old_url']} {r['new_url']} permanent")
  #]]]
  redir /videos/crossness_flywheel.mp4 /files/2017/crossness_flywheel.mp4 permanent
  redir /2021/12/2021-in-reading/ /2021/2021-in-reading/ permanent
  redir /2022/12/print-sbt/ /til/2022/print-sbt/ permanent
  #[[[end]]]
}

This is a powerful change – unlike the original Caddyfile, it’s easy to write scripts that insert entries in this external JSON file, and now I can programatically update this file.

My scripts that are rearranging my URLs can populate redirects.json, then I only need to re-run Cog and I have a complete set of redirects in my Caddyfile.

I usually run Cog with two flags:

  • -r writes the output back to the original file, and
  • -c adds a checksum to the end marker, like [[[end]]] (sum: Rwh4n2CfQD). This checksum allows Cog to detect if the output has been manually edited since it last processed the file – and if so, it will refuse to overwrite those changes. You have to revert the manual edits or remove the checksum.

You can also run Cog with a --check flag, which checks if a file is up-to-date. I run this as a continuous integration task, to make sure I’ve updated my files properly.

Why I like Cog

What separates Cog from traditional templating engines like Jinja2 or Liquid is that it operates entirely in-place on the original file. Usually, you have a source template file and a build step which produce a separate output file, but with Cog, the source and the result are stored in the same document. Storing templates in separate files is useful for larger projects, but it’s overkill for something like my Caddyfiles.

Having everything in a single file makes it easy to resume working on a file managed with Cog. I don’t need to remember where I saved the build script or the template; I can operate directly on that single text file. If I come back to this project in six months, the instructions for how the file is generated are right in front of me.

The design also means that I’m not locked into using Cog. At any point, I could delete the Cog comments and still have a fully functional file.

Cog isn’t a replacement for a full-blown templating language, and it’s not the right tool for larger projects – but it’s indispensable for small amounts of automation. If you’ve never used it, I recommend giving it a look – it’s a handy tool to know.