All Features
A complete checklist of rust-web-server capabilities organized by category, with feature flag annotations.
This page is a single-page reference for everything the crate ships. Items that require an opt-in feature flag are annotated with the flag name in backticks.
Server / Protocol
- HTTP/1.1 — keep-alive, chunked transfer encoding, gzip compression, ETags, range requests, 30-second idle read timeout, 30 s to receive a whole request head (
408) - HTTP/2 — ALPN negotiation, full multiplexing, forbidden-header stripping,
Alt-Svcadvertisement —http2 - HTTP/3 / QUIC — UDP listener via
quinn,h3+h3-quinn, QUIC connection-level SNI —http3(default) - TLS —
rustlswithaws-lc-rscrypto backend; no system OpenSSL dependency; FIPS-compatible —http2 - mTLS — client certificate verification via
WebPkiClientVerifier; setRWS_CONFIG_TLS_CLIENT_CA_FILE—http2 - Virtual-host routing — per-domain TLS certificates via
SniCertResolver;.with_host("example.com")on anyRouter—http2 - HTTP → HTTPS redirect —
Server::run_redirect()listens on a second port and issues301responses —http2 - ACME / automatic TLS — automatic certificate provisioning compatible with Let's Encrypt —
acme - Hot reload —
SIGHUP(orPOST /admin/config/reload) reloads CORS rules, rate limits, log format, and TLS certs without restarting —http2 - Graceful shutdown —
SIGINT/SIGTERMdrain in-flight requests before exit;SERVER_READYflag cleared on shutdown - Gzip compression — applied automatically based on
Accept-Encodingto text bodies of at least 512 bytes (compression::MIN_COMPRESS_SIZE) - Static file serving — a directory with no
index.htmlrenders a dark/light-adaptive directory listing page by default; files over 256 KiB streamed (sendfile(2)on plain TCP); ETag +Last-Modifiedwith304revalidation,Cache-Control: no-cacheby default (RWS_CONFIG_STATIC_CACHE_CONTROL) - SPA fallback —
RWS_CONFIG_SPA_FALLBACK=index.htmlserves a fallback file for unmatched routes instead of404, for React Router/Vue Router/etc. deep links; opt-in, scoped by an extension heuristic andRWS_CONFIG_SPA_FALLBACK_EXCLUDE_PREFIXES - Thread pool (http1 only) — hand-rolled
ThreadPool; connection-per-thread; no tokio —http1 - Tokio async runtime — used for
http2andhttp3builds;http1is fully synchronous - Request framing — heads up to 64 KiB across any number of reads (
431beyond), pipelining, chunked request bodies;Content-Length+Transfer-Encodingor conflicting lengths are rejected (400) so a proxy in front can't be desynced (request smuggling) Expect: 100-continue—100 Continueis sent before the body is read, after the body-size check- Max request body size —
RWS_CONFIG_MAX_BODY_SIZE_IN_BYTESrejects oversized bodies with413before buffering, on HTTP/1.1, HTTP/2 and HTTP/3 - Outbound HTTP client —
Client(sync) andAsyncClient(http2); HTTPS withhttp-clientorhttp2; redirects,.form(),.body_json() - WebSocket (RFC 6455) — handshake and frame codec built in
- WASM guest component —
rws-wasm-shimrunsApp/Router/middleware inside awasi:http/proxycomponent (Wasmtime, Spin, Fastly Compute); see WASM
Routing & App Building
App— zero-config entry point; wraps all built-in controllers (static files, CORS, health, metrics, MCP, …)App::with_state(S)— state-aware dynamic router;Arc<S>shared across all handlers; builder methods.get(),.post(),.put(),.patch(),.delete()App::with_async_state(S)— same API withasync fnhandlers —http2routes!macro — ergonomic route registration:GET "/path" => handler—macrosRouter— standalone path-based router with named parameters (:name) and trailing wildcards (*name);PathParams::get(name)Controllertrait — low-levelis_matching/processinterface; register inApp::executefor maximum control- Typed extractors —
Body(raw bytes),BodyText(UTF-8),Query(parsed query string),RequestHeaders(case-insensitive header map) - Typed errors —
AppErrorenum (BadRequest,Unauthorized,Forbidden,NotFound,Conflict,UnprocessableEntity,TooManyRequests,Internal) withIntoResponsetrait Middlewaretrait —handle(request, connection, next) -> Result<Response, String>; compose via.wrap(layer)WithMiddleware<A>— wraps anyApplication; layers applied in push order (first-pushed is outermost)- JSON (de)serialization —
serde/serde_jsonintegration (e.g.http_client::Response::json::<T>()) —serde - Tera templates — render
.htmltemplates with aserde_json::Valuecontext viatera::render—tera - In-process test client —
TestClient::new(app)dispatches requests without opening a TCP socket; use in unit and integration tests - WebSocket — RFC 6455 handshake and frame codec built in; no third-party WebSocket library
- SSE (Server-Sent Events) —
Ssebuilder for streaming events; ideal for streaming AI model output - Background scheduler —
Scheduler::new().every(duration, fn).cron("…", fn).start() - Background job queue —
JobQueue(in-memory) orPersistentJobQueue(crash-safe, model-backed); retry with exponential backoff —jobs - File / object storage —
Storagetrait;LocalStorage(disk),S3Storage(AWS S3, R2, MinIO via AWS SigV4, no SDK), orAzureBlobStorage(Azure Blob via Shared Key/Managed Identity, no SDK) —storage-local/storage-s3/storage-azure - OpenAPI / Swagger —
.openapi(OpenApiConfig)generatesGET /openapi.json+GET /docs(Swagger UI) from registered routes —openapi - Email / SMTP —
mailermodule, plain SMTP; STARTTLS/SMTPS also needshttp-clientorhttp2—mailer - Outbound HTTP client —
http_client::Client(always compiled for plain HTTP); HTTPS without the server TLS stack —http-client - Per-route timeouts —
with_timeout/with_timeout_state/with_timeout_asyncwrap a handler with its own deadline;TimeoutLayerfor a whole app; config-driven proxy'stimeout_ms - Request ID middleware —
RequestIdLayerinjects/echoesX-Request-Idon every request and response;RequestIdextractor to read it
Proxy & Gateway
ReverseProxy— HTTP/1.1 reverse proxy middleware; round-robin backend selectionH2ReverseProxy— HTTP/2 upstream proxy; bridges sync middleware into async via an isolated-runtime helper (works under any tokio runtime flavor) —http2GrpcProxy— wrapsH2ReverseProxy; filters onContent-Type: application/grpc*—http2TcpProxy— standalone L4 TCP proxy; bidirectionalstd::io::copyrelay; round-robin backendsUdpProxy— standalone UDP datagram proxy; per-datagram ephemeral socket; round-robin backendsWsProxy— standalone WebSocket proxy; relays raw bytes after upgrade handshake- Config-driven proxy mode —
rws.config.tomlwith[[upstream]]+[[route]]sections; no code required - Health checking — per-upstream background health checker; dead backends removed automatically; configurable thresholds and intervals
- Load balancing —
round_robin,random,ip_hash, orleast_connectionsacross live backends RewriteLayer— rewrite request headers, URI, and response headers / body / status code per route; regex URI rewrites viarequest_uri_regex_rewrite—rewrite-regex- Per-route rate limiting —
PerRouteRateLimitin config-driven mode;RateLimitLayerin code - Per-route Bearer auth —
BearerAuthMiddlewarein config-driven mode;JwtLayerin code - Route matching — host, path prefix, exact path, HTTP method,
Content-Typeprefix; first-match wins - Service discovery —
BackendPoolwithStatic/EnvPrefix/File/Dns/DnsSrv(weighted)/Consul/Docker(label value as address) sources on a poll loop, plusEtcdWatchon a dedicated live watch stream - Circuit breaker + retries —
CircuitBreaker(Closed → Open → HalfOpen, capped probes),RedisCircuitBreaker(state shared across instances),RetryLayeron 502/503/504;ReverseProxy::with_circuit_breaker;rws_circuit_breaker_statemetric - Canary / traffic splitting —
CanaryLayerwith smooth weighted round-robin, live weight updates,BackendPoolgroups - Kubernetes Ingress —
KubernetesIngressWatcherwatches the API (Exact/PrefixpathType,ingressClassName),.from_service_account()in-cluster - Private-CA upstreams —
[[upstream]] tls_ca_file/ReverseProxy::tls_ca_file - Non-blocking proxying — config routes without middleware forward on the event loop, holding no thread
Security
RateLimitLayer— sliding-window rate limiter keyed by client IP; global singleton viarate_limit::global(); returns429when budget exceededRedisRateLimiter— fixed-window limiter in Redis for a budget shared across instancesBasicAuthLayer— HTTP Basic authentication middleware —authJwtLayer— JWT Bearer token validation middleware (HS256) —auth; RS256/ES256 against a static public key via::rs256/::es256—auth-asymmetricForwardAuthLayer— delegates the allow/deny decision to an external HTTP service (Traefik/nginxauth_requeststyle) —authIpFilter— allowlist / denylist middleware keyed by client IPcrypto— Argon2id password hashing and verification;signed_cookie/verify_signed_cookie(HMAC-SHA256) andencrypted_cookie/decrypt_cookie(AES-256-GCM) —crypto- CSRF tokens — secure random token generation and validation —
csrf - SSO / OAuth 2.0 / OIDC —
OidcAuthlogin flow, RS256/ES256id_tokenverification via JWKS, outbound HTTPS token exchange —sso - OAuth 2.0 Authorization Server —
AuthServer: rws issues its own HS256 tokens (/oauth/token,/oauth/authorize) —sso-server - SAML 2.0 Service Provider —
SamlSp: metadata, login, ACS, logout —sso-saml - Webhook verification —
verify_webhook_signaturefor GitHub, Shopify, Stripe —webhook - Secrets manager references —
secrets::resolve()forvault://,aws-sm://,azkv://—secrets - CORS — built-in CORS controller; configurable origins, methods, and headers; hot-reloadable
- mTLS — mutual TLS client certificate verification —
http2 - No OpenSSL — TLS via
rustls+aws-lc-rs; fully static binaries
Observability & Ops
/healthz— liveness probe; always200 OKwhile the server is up/readyz— readiness probe; returns503after graceful shutdown begins (SERVER_READYflag)/metrics— Prometheus text-format scrape endpointMetricsLayer— per-routerws_route_requests_total{method,path,status}counters andrws_route_duration_seconds{method,path}histograms- Global metrics —
record_request(),record_error(),connection_open()/connection_close()counters OtelLayer— OpenTelemetry trace propagation middlewareCacheLayer— response caching middleware- Structured logging —
RWS_CONFIG_LOG_FORMAT=json(default),combined, oroff; hot-reloadable - Config hot reload —
SIGHUPorPOST /admin/config/reloadreloads CORS, rate limits, log format, TLS certs; no restart required - Kubernetes-ready —
/healthz,/readyz,/metricsall built in; gracefulSIGTERMdrain; static binary with no system dependencies rws --check— validatesrws.config.toml(unknown keys, bad values, TLS files, undeclared upstreams, unset auth secrets) and exits 1 on errors, likenginx -t;--format=json- Helm chart —
helm/rws/ - Background scheduler — fixed-rate, fixed-delay and 6-field cron tasks
- Job queue —
JobQueue(in-memory) andPersistentJobQueue(model-backed); retries with exponential backoff —jobs - Email —
Mailerover SMTP, STARTTLS or SMTPS; text + HTML —mailer
AI & MCP
McpServer— implementsApplication; serves the MCP Streamable HTTP protocol (POST /mcp, JSON-RPC 2.0)- Tool registration —
.tool(name, description, schema, handler)— any function returning a string - Resource registration —
.resource(uri_template, name, description, handler) - Prompt registration —
.prompt(name, description, handler) - Bearer auth gate —
.require_bearer(token)gates all MCP requests behind a static token - Fallthrough —
.wrap(app)passes non-MCP requests to anotherApplication - 20 built-in rws tools in the binary — config, metrics, routes, cache, rate limits, CORS, static files, reload, maintenance mode, IP blocklist, feature flags, recent requests/errors (see MCP)
- SSE streaming —
Ssebuilder for streaming AI tokens to browser clients - Protocol coverage — session context (
.tool_with_context), tool annotations, image/embedded content, JSON-RPC batches, list pagination,GET /mcpSSE push,logging/setLevel, dynamic registration withlist_changed, progress,completion/complete, cancellation,resources/subscribe, server-side sampling,roots/list, async tools (http2), OAuth (.require_oauth,sso)
Database / ORM
All ORM features require at least one of model-sqlite, model-postgres, or model-mysql (async, backed by sqlx; several can be compiled in, pick one with RWS_DB_BACKEND).
#[derive(Model)]— generatesimpl Model,Struct::repository(),Struct::query()—macros- Table and column mapping —
#[table(name = "…")],#[column(name = "…")],#[primary_key],#[primary_key(auto_increment)],#[ignore] DbPool— asyncsqlxpool:execute,query_rows,query::<T>,query_raw,begin(→DbTransactionwithcommit/rollback),transaction(closure),migrateRepository<T, ID>—save(INSERT or UPDATE by primary key),save_all,find_by_id,find_all,delete_by_id,delete_all_by_id,count,exists_by_idQueryBuilder— fluentwhere_eq,filter,order_by,limit,offset,paginate,fetch_all,fetch_one,count,delete,update; placeholder tokens auto-converted to?(SQLite/MySQL) or$N(PostgreSQL)- Migrations — reads
*.sqlfiles in lexicographic order;_schema_migrationstable tracks applied versions; each migration wrapped in a transaction - Relations —
HasMany<T>,HasOne<O>,BelongsTo<O>with explicit.load(&pool); no lazy loading, no hidden N+1 queries - SQLite —
sqlxSQLite driver —model-sqlite - PostgreSQL —
sqlxPostgres driver —model-postgres - MySQL / MariaDB —
sqlxMySQL driver —model-mysql
Sessions & Cookies
CookieJar/SetCookie— parse request cookies, write every RFC 6265 attribute- Sessions —
SessionStore(in-memory TTL),DbSessionStore(model-backed),RedisSessionStore
File / Object Storage
Storagetrait —put/get/delete/url; swap backends without touching handlersLocalStorage— local disk —storage-localS3Storage— AWS S3, Cloudflare R2, MinIO via SigV4; static keys or EKS IRSA / ECS task role / EC2 IMDSv2; no AWS SDK —storage-s3AzureBlobStorage— Shared Key or Managed Identity; no Azure SDK —storage-azure
Secrets
secrets::resolve—vault://path#field,aws-sm://name[#field],azkv://vault/secretresolved over HTTPS —secrets- Any
RWS_CONFIG_*can be a reference —resolve_env_vars()runs inServer::setup(); a broken reference fails startup instead of running with the literal string
Dependency Injection
Container—TypeId-keyed service store for concrete types anddyn Traitobjectsregister::<T>(value)— wraps inArc<T>, keyed byTypeId::of::<T>()provide::<T: ?Sized>(Arc<T>)— stores trait objects directly- Named services —
get_named::<T>(name)/register_named::<T>(name, value)for multiple instances of the same type App::with_state(container)— pass the container directly as state; it'sSend + Sync + 'staticlike any other state type, no wrapping neededinto_arc()— for sharing one container across multiple hand-builtApplications outside ofwith_state- No external dependencies