etag
A tiny, zero-dependency generator for RFC 7232 HTTP ETags from strings, Buffers, or fs.Stats.
Repository Health
Technical Analysis
etag is a minimal, single-purpose Node.js module maintained by the jshttp organization that generates HTTP ETag header values as defined in RFC 7232. Given a string, Buffer, or fs.Stats object, it produces a strong or weak entity tag suitable for setting directly on an HTTP response, letting servers and frameworks implement conditional requests and caching without hand-rolling hash logic.
Under the hood it hashes the entity with SHA-1 via Node’s built-in crypto module and encodes a truncated base64 digest alongside the entity’s length, with a fast path for empty content and a mtime/size-based tag for filesystem stats. It has no runtime dependencies, ships as a single file, and is one of the most widely used building blocks in the Node HTTP ecosystem, pulled in transitively by Express, Koa’s send, and countless static-file and compression middlewares.
What You Get
- A single etag(entity, options) function that returns a ready-to-use ETag header value
- Automatic strong/weak tag selection based on input type, with an explicit weak override
- A fast-path empty-entity tag and dedicated handling for fs.Stats (mtime + size) without hashing file contents
- Zero runtime dependencies and a footprint of one small file, safe to vendor into any Node HTTP stack
Common Use Cases
- Setting the ETag response header in a custom HTTP server or middleware for conditional caching
- Powering static-file servers that need cheap, stat-based ETags instead of hashing whole files on every request
- Generating body-based ETags for API responses so clients can send If-None-Match and get 304s
- Acting as the ETag primitive underneath higher-level frameworks and libraries (e.g. Express, send) that expose their own caching options
Under The Hood
Architecture etag ships as a single CommonJS module (index.js, ~130 lines) with no internal layering: a public etag(entity, options) function delegates to two private helpers, entitytag for strings/Buffers and stattag for fs.Stats-like objects, selected via a duck-typed isstats check. There is no class hierarchy, no dependency injection, and no internal data flow beyond argument validation, type dispatch, hash/format, and return; the entire module is effectively a pure function with two private helper functions co-located in the same file. This flat, single-responsibility structure means the only thing that could break by changing the core abstraction is the tag format itself (the “<length>-<hash>” / “<size>-<mtime>” string shapes), which downstream consumers parse via regex (as its own test suite does) rather than a structured type.
Tech Stack The module targets any Node.js runtime from 0.6 onward per its engines field and has zero runtime dependencies, relying solely on Node’s built-in crypto (SHA-1 hashing) and fs.Stats (type-checking). Its devDependencies are entirely for tooling: mocha and nyc for testing and coverage, eslint with the standard config plus import/node/promise/markdown plugins for linting (including linting fenced code in the README itself), and benchmark/beautify-benchmark/seedrandom for the benchmark suite under benchmark/. Distribution is npm-only (files restricts the published package to LICENSE, HISTORY.md, README.md, and index.js), and CI runs via GitHub Actions with an additional CodeQL security-scanning workflow and Dependabot for both npm and Actions updates.
Code Quality Test coverage lives in test/test.js and is run with mocha —reporter spec —bail —check-leaks, asserting exact ETag output strings for string, Unicode, Buffer, empty-input, real fs.Stats, and duck-typed fake-stats cases, plus both values of the weak option; nyc produces lcov/text coverage in the test-ci/test-cov scripts. Error handling is minimal but explicit: invalid input throws TypeError with a specific message rather than failing silently, and both error paths (missing entity, wrong type) are asserted in tests. The code has no TypeScript types of its own (a community @types/etag package exists separately on DefinitelyTyped) but uses consistent JSDoc @public/@private/@param/@return annotations throughout, and is linted with ESLint’s standard config enforced in CI alongside the CodeQL workflow; there is no separate CONTRIBUTING.md, but the small, fully-tested surface area keeps the bar for contribution low.
API Design The public API is a single function, etag(entity, [options]), requiring no setup, no configuration object beyond one optional weak boolean, and no constructor or instance state, call it and get a string back. Defaults are chosen to match RFC 7232 expectations automatically (strong tags for content, weak tags for stats) so the common case needs no options at all, while the weak override covers the one case callers legitimately need to control. Documentation is a single README section with a runnable snippet (res.setHeader(‘ETag’, etag(body))), which is enough given the API’s size; the main friction for a newcomer is knowing when to pass an fs.Stats object versus a string/Buffer, and that decision is exactly the branch the module makes for you via type detection.
Used by 3 apps in this directory
Jitsu
Data Engineering
Open-source, fully-scriptable data ingestion engine that streams events from web, apps, and APIs to any data warehouse in real time.
Logto
Authentication
Open-source auth infrastructure for SaaS and AI apps with OIDC, SAML, and RBAC
RSSHub
Automation · Social Media
Turn any website into an RSS feed — social media, streaming platforms, and niche sites all become subscribable in seconds.