From 5b4963d2093eee1555fc622b0b25559addf21359 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 30 May 2026 16:13:42 +0200 Subject: [PATCH 1/6] Add Cookies recipe documenting the cookie jar format --- astro.config.mjs | 1 + src/content/docs/recipes/cookies.md | 50 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/content/docs/recipes/cookies.md diff --git a/astro.config.mjs b/astro.config.mjs index 9788d1c..60af1b8 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -63,6 +63,7 @@ export default defineConfig({ items: [ "recipes/anchors", "recipes/caching", + "recipes/cookies", "recipes/excluding-links", "recipes/excluding-paths", "recipes/local-folder", diff --git a/src/content/docs/recipes/cookies.md b/src/content/docs/recipes/cookies.md new file mode 100644 index 0000000..351c597 --- /dev/null +++ b/src/content/docs/recipes/cookies.md @@ -0,0 +1,50 @@ +--- +title: Cookies +--- + +Some websites require cookies to return the right response. For example, a site +might set a session cookie on the first request and expect it on the next one. +lychee can persist cookies between requests using a cookie jar. + +## Using a cookie jar + +Pass `--cookie-jar` with a path to a JSON file: + +```bash +lychee --cookie-jar cookies.json https://example.com +``` + +lychee loads existing cookies from the file before checking, stores any cookies +the server sets during the run, and writes them back to the same file when it's +done. If the file doesn't exist yet, lychee creates it. + +This means you can run lychee once to collect cookies and reuse them on later +runs by pointing at the same file. + +## File format + +The cookie jar is a plain JSON array. Each entry is a single cookie: + +```json +[ + { + "raw_cookie": "session=abc123; Secure; Path=/; Domain=example.com; Expires=Tue, 03 Aug 2100 00:38:37 GMT", + "path": ["/", true], + "domain": { "Suffix": "example.com" }, + "expires": { "AtUtc": "2100-08-03T00:38:37Z" } + } +] +``` + +The fields are: + +- `raw_cookie`: the full `Set-Cookie` string. Everything else is derived from this. +- `path`: the cookie path, plus whether it was set explicitly (`true`) or defaulted from the request URL (`false`). +- `domain`: `{ "Suffix": "example.com" }` for a `Domain=` cookie (matches subdomains too), `{ "HostOnly": "example.com" }` for an exact host, or `"NotPresent"`. +- `expires`: `{ "AtUtc": "2100-08-03T00:38:37Z" }` for a fixed expiry, or `"SessionEnd"` for session cookies. + +:::note +Only unexpired, persistent cookies are written back, so session cookies and +expired ones won't appear in the saved file. You can hand-edit the file as long +as it stays a valid JSON array. +::: From 4dd6e7802cf3b9a4cfb7a0865001065d9918d7d1 Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Sat, 30 May 2026 16:18:02 +0200 Subject: [PATCH 2/6] Cleanup. Add reference link for 'Set-Cookie'. --- src/content/docs/recipes/cookies.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/docs/recipes/cookies.md b/src/content/docs/recipes/cookies.md index 351c597..1e62ab0 100644 --- a/src/content/docs/recipes/cookies.md +++ b/src/content/docs/recipes/cookies.md @@ -19,7 +19,7 @@ the server sets during the run, and writes them back to the same file when it's done. If the file doesn't exist yet, lychee creates it. This means you can run lychee once to collect cookies and reuse them on later -runs by pointing at the same file. +runs by pointing at the same file, which can save some manual work! ## File format @@ -38,7 +38,7 @@ The cookie jar is a plain JSON array. Each entry is a single cookie: The fields are: -- `raw_cookie`: the full `Set-Cookie` string. Everything else is derived from this. +- `raw_cookie`: the full [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie) string. - `path`: the cookie path, plus whether it was set explicitly (`true`) or defaulted from the request URL (`false`). - `domain`: `{ "Suffix": "example.com" }` for a `Domain=` cookie (matches subdomains too), `{ "HostOnly": "example.com" }` for an exact host, or `"NotPresent"`. - `expires`: `{ "AtUtc": "2100-08-03T00:38:37Z" }` for a fixed expiry, or `"SessionEnd"` for session cookies. From 2ba4ea117e5b704203c61e3fa7768622242f7bb3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 7 Jun 2026 12:05:17 +0200 Subject: [PATCH 3/6] Cite cookie_store crate and RFC 6265 in cookie format docs --- src/content/docs/recipes/cookies.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/content/docs/recipes/cookies.md b/src/content/docs/recipes/cookies.md index 1e62ab0..f5afad6 100644 --- a/src/content/docs/recipes/cookies.md +++ b/src/content/docs/recipes/cookies.md @@ -23,7 +23,10 @@ runs by pointing at the same file, which can save some manual work! ## File format -The cookie jar is a plain JSON array. Each entry is a single cookie: +The cookie jar is a plain JSON array. Each entry is a single cookie, serialized +in the format used by the [`cookie_store`](https://docs.rs/cookie_store/latest/cookie_store/struct.Cookie.html) +crate, which lychee uses to manage cookies. Cookie storage and matching follow +the rules of [RFC 6265](https://datatracker.ietf.org/doc/html/rfc6265): ```json [ From 3623bca23fce4b75696491edcac39db9a6cd7af4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 7 Jun 2026 12:05:26 +0200 Subject: [PATCH 4/6] Explain raw_cookie overlap with parsed cookie fields --- src/content/docs/recipes/cookies.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/content/docs/recipes/cookies.md b/src/content/docs/recipes/cookies.md index f5afad6..e1bdc5b 100644 --- a/src/content/docs/recipes/cookies.md +++ b/src/content/docs/recipes/cookies.md @@ -46,6 +46,12 @@ The fields are: - `domain`: `{ "Suffix": "example.com" }` for a `Domain=` cookie (matches subdomains too), `{ "HostOnly": "example.com" }` for an exact host, or `"NotPresent"`. - `expires`: `{ "AtUtc": "2100-08-03T00:38:37Z" }` for a fixed expiry, or `"SessionEnd"` for session cookies. +The `raw_cookie` string and the other fields overlap because that's how the +`cookie_store` crate represents a cookie: it keeps the original `Set-Cookie` +header alongside the `path`, `domain`, and `expires` values it parsed out of it. +Those parsed fields are what it uses to decide whether a cookie applies to a +given request, following RFC 6265. + :::note Only unexpired, persistent cookies are written back, so session cookies and expired ones won't appear in the saved file. You can hand-edit the file as long From 7858037dd742ec9538d0aa5b7bdc858a0b9e48e0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 7 Jun 2026 12:05:37 +0200 Subject: [PATCH 5/6] Clarify behavior when hand-editing the cookie jar --- src/content/docs/recipes/cookies.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/content/docs/recipes/cookies.md b/src/content/docs/recipes/cookies.md index e1bdc5b..9c04fef 100644 --- a/src/content/docs/recipes/cookies.md +++ b/src/content/docs/recipes/cookies.md @@ -54,6 +54,9 @@ given request, following RFC 6265. :::note Only unexpired, persistent cookies are written back, so session cookies and -expired ones won't appear in the saved file. You can hand-edit the file as long -as it stays a valid JSON array. +expired ones won't appear in the saved file. + +When editing the file by hand, keep each entry in the format shown above, since +lychee parses it back into the same structure. If lychee cannot parse the file's +contents, it reports an error and aborts rather than ignoring the bad entries. ::: From 559c94f0cd3c35bc747db72c4241f0c0fdd2f250 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 7 Jun 2026 12:05:44 +0200 Subject: [PATCH 6/6] Warn that the cookie jar format may change between releases --- src/content/docs/recipes/cookies.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/content/docs/recipes/cookies.md b/src/content/docs/recipes/cookies.md index 9c04fef..bf73b37 100644 --- a/src/content/docs/recipes/cookies.md +++ b/src/content/docs/recipes/cookies.md @@ -52,6 +52,12 @@ header alongside the `path`, `domain`, and `expires` values it parsed out of it. Those parsed fields are what it uses to decide whether a cookie applies to a given request, following RFC 6265. +:::caution +This format is not a stable API and may change between lychee releases. If you +depend on the file layout, check the [release notes](https://github.com/lycheeverse/lychee/releases) +when upgrading. +::: + :::note Only unexpired, persistent cookies are written back, so session cookies and expired ones won't appear in the saved file.