Loading search…

Explain with AI
Edit this page

Cookies & Anti-Bot Handling

Last updated

Understand cookie storage, session lifetimes, and the available HTTP settings.

On this page

comic-dl uses a SQLite cookie store with a subset of RFC 6265 behavior. Whether a cookie survives a restart depends on how it enters the store and whether it has an expiry.

Platform Location
Linux ~/.config/comic-dl/cookies.db
macOS ~/Library/Application Support/comic-dl/cookies.db
Windows %LOCALAPPDATA%\comic-dl\cookies.db

The jar uses SQLite in WAL mode. Cookies imported from an HTTP session are persisted only when they have an unexpired expiry; cookies without an expiry stay in memory for that process. Explicit storage through CookieJar.set writes to disk even without an expiry, so “session cookies are never persisted” is not a guarantee of the store.

Managing cookies

comic-dl cookie ls                     # list all stored cookies
comic-dl cookie ls kagane.to           # list cookies for a host
comic-dl cookie ls --json              # machine-readable output
comic-dl cookie set <host> <name> <value> [--expires <epoch>]
comic-dl cookie clear                  # clear all cookies
comic-dl cookie clear kagane.to        # clear cookies for a host
Warning

cookie set passes the value on the command line, making it visible in shell history and to other users via ps. Prefer letting the tool capture cookies itself (via the webview solver), or clear the value afterwards.

Disabling cookies

comic-dl -u <URL> --no-cookie          # stateless for one run

Or in config:

[http]
cookie-jar = false

When the cookie jar is disabled, the scrape response cache is also bypassed.

Cloudflare challenge handling

When a request returns a Cloudflare challenge (403/503 interstitial), comic-dl can clear the stale cookie and retry once. Challenge solving is opt-in: by default (solver = "off") a challenged site fails with a solver hint instead of being retried.

Solver modes

Mode Flag Behavior
off solver = "off" (default) Never retry challenged requests; the site fails with a hint.
auto --solver auto Detect challenge → clear stale cookie → retry. Opens webview if available, falls back to impersonation.
impersonation --solver impersonation Never open webview. Rely on TLS/HTTP fingerprint profile.
webview --solver webview Force system webview. Falls back to impersonation if it cannot start.

Enable a mode per run with --solver <mode>, in config with [http] solver = "<mode>", or per host with [sources."<host>"] mode = "<mode>".

Webview requirements

The webview solver is bundled with the package. Your platform needs a working backend:

Platform Backend Notes
Windows Edge WebView2 Built-in on Windows 10+
macOS WKWebView Built-in
Linux WebKitGTK Requires PyGObject + WebKitGTK from distro packages

On headless Linux, the tool falls back to xvfb-run when available, then to impersonation-only.

A visible window is shown because Cloudflare fingerprints hidden/offscreen renders. A stored cf_clearance is reused for its full validity (~1–2 hours).

TLS fingerprint binding

Some sites (e.g. kagane.to) bind cf_clearance to the exact TLS fingerprint. The webview session serves requests from inside the page so the fingerprint matches. Plain curl_cffi replay fails with 403 on these sites.

Impersonation profiles

TLS/HTTP fingerprint profiles simulate a real browser:

comic-dl -u <URL> --impersonate chrome131

Default: chrome146. Configurable via [http] impersonate or --impersonate.

Deprecated profiles (chrome99–104, edge99, edge101, safari15_3/15_5) are allowed with a warning.

Rate limiting

Per-site throttling spaces out requests. See Rate Limiting for configuration and concurrency details. Defaults exist for:

Host Rate
kagane.to 1.5 req/s
kstatic.to 2.0 req/s
e-hentai.org 2.0 req/s

Override or add hosts under [http] rate. Per-host overrides in [sources."<host>"] take highest precedence.

Disable entirely with --no-rate or [http] rate-enabled = false.

e-hentai

The /s/ image-page fetches run at 2 req/s matching the default. If you see “image limit reached” errors, lower e-hentai.org toward 1.0. Throttled responses (HTTP 509) are detected and retried automatically.

Encryption at rest

Cookie values are encrypted in the jar with AES-256-GCM so a reader who can see cookies.db does not get working session tokens. The key is a random 32-byte value held in the OS keyring and generated on first use.

Mode Key source
auto (default) $COMIC_DL_COOKIE_KEY, then the OS keyring (create + store a fresh key on first use)
keyring OS keyring only; never read the environment variable
off Plaintext jar — for CI/throwaway runs that want no key source
[http]
cookie-encryption = "auto"

For headless machines (CI, servers) set the env key — keep it in your secret store, not in a config file:

# generate once:
uv run python -c "import base64,secrets;print(base64.urlsafe_b64encode(secrets.token_bytes(32)).decode())"
# then export it (never commit it):
export COMIC_DL_COOKIE_KEY="<base64 above>"

Encrypted values are stored as enc1. + base64url(nonce + ciphertext + tag).

  • A stored value without the enc1. prefix is legacy plaintext: it still reads, and the next write to that cookie upgrades it to the envelope. There is no migration step.
  • An enc1. value that fails authentication or decryption (key changed or tampered) is dropped, never served, and never raises.
  • If no key source is available in auto mode, comic-dl warns once per process and runs a plaintext jar — it never fails a download because the keyring is missing.
  • The keyring entry is service comic-dl, username cookie-db-key.

To rotate or revoke the key: comic-dl cookie clear, then delete the keyring entry (or unset $COMIC_DL_COOKIE_KEY). Existing enc1. rows become unreadable and are dropped on next use — that is the point.

Note

At-rest encryption keeps cookie values hidden from file readers. It is not protection against running the tool as someone it is not installed for — the key lives in the same OS account.