Small, focused Ring middleware for security and observability.
This library collects a set of middleware components used across Sturdy Statistics web services. Each middleware does one thing, is explicit, and is intended to be composed deliberately rather than enabled wholesale.
This library reflects the needs and opinions of Sturdy Statistics. We may not accept feature requests that dilute its focus.
These are not intended to be framework defaults; rather, they encode specific operational and security decisions that we want to apply consistently across our applications.
Explicit code Middleware should make policy obvious at the call site.
Idempotent and safe Middleware should be safe to apply once or multiple times without surprising behavior.
Fail closed where appropriate Deny on missing or ambiguous input, especially for auth-related or state-changing endpoints.
Cheap by default No unnecessary allocation, parsing, or reflection on hot paths.
Add to deps.edn:
{:deps {com.sturdystats/sturdy-middleware {:mvn/version "VERSION"}}}
| Middleware | Purpose | Typical scope |
|---|---|---|
wrap-request-id | Stable request correlation | All requests |
wrap-max-request-size | Reject oversized uploads early | Upload / API endpoints |
wrap-require-same-origin | CSRF protection (tolerant) | Authenticated endpoints |
wrap-require-same-origin-strict | CSRF protection (strict) | Auth endpoints |
wrap-nostore | Prevent caching of private responses | Logged-in pages, dashboards |
wrap-nostore-on-errors | Prevent caching of error responses | All requests |
with-vary-cookie | Prevent cache mixing across users | Logged-in pages |
with-noindex | Prevent indexing by search engines | Private or internal pages |
wrap-request-idAttach a request identifier to every request and response.
X-Request-Id, X-Correlation-Id, traceparent):request-id in the Ring requestX-Request-Id)(wrap-request-id handler)
wrap-max-request-sizeReject requests with a Content-Length exceeding a configured limit.
Content-Length(wrap-max-request-size handler (* 10 1024 1024)) ; 10 MB
wrap-require-same-origin (tolerant)Origin header(wrap-require-same-origin handler)
wrap-require-same-origin-strictOrigin(wrap-require-same-origin-strict handler)
wrap-nostorePrevents caching of user-specific or sensitive responses.
(wrap-nostore handler)
wrap-nostore-on-errorsApplies no-store headers to error responses and mutable requests.
(wrap-nostore-on-errors handler)
with-vary-cookieEnsures responses vary on Cookie without duplication.
(with-vary-cookie response)
with-noindexPrevents search engines from indexing private pages.
(with-noindex response)
(-> handler
wrap-request-id
wrap-max-request-size
wrap-require-same-origin
wrap-nostore-on-errors
wrap-nostore)
(-> handler
wrap-request-id
wrap-require-same-origin-strict
wrap-nostore)
This library exists to centralize security-affecting middleware so that:
Designed for production use in long-running services.
wrap-max-request-size exposes a dynamic rendering hook that applications may rebind to integrate with their own error views.
By default, oversized requests return:
Connection: closeThis default is safe and dependency-free, but is likely too basic for production use. If you use this library, you will want to re-bind it.
Applications may rebind *render-too-large* to customize either the response body or entire response.
The hook is called with a context map containing:
{:code 413
:title "Content too large"
:blurb "Upload failed."
:message "... human-readable explanation ..."
:request-id "abc123" ; if present
:request <ring-request-map>
:max-upload-bytes 10485760}
The function may return either:
(require
'[sturdy.middleware.request-size :as rs]
'[ring-errors.views :as err-v])
(def app
(binding [rs/*render-too-large*
(fn [{:keys [message request-id]}]
(err-v/error-page
{:code 413
:title "Content too large"
:blurb "Upload failed."}
{:message message
:id request-id}))]
(-> handler
(rs/wrap-max-request-size (* 10 1024 1024)))))
If you prefer a single global configuration:
(alter-var-root
#'rs/*render-too-large*
(constantly
(fn [ctx]
(my-custom-413-view ctx))))
The render hook can inspect request headers (e.g. Accept) and return JSON for API clients while keeping HTML for browsers.
(fn [{:keys [request message]}]
(if (= "application/json"
(get-in request [:headers "accept"]))
{:status 413
:headers {"Content-Type" "application/json"}
:body {:error "payload-too-large"
:message message}}
(html-view message)))
Apache License 2.0
Copyright © Sturdy Statistics
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |