rws
Website: rws8.tech
A dependency-minimal Rust web platform: HTTP/1.1, HTTP/2, and HTTP/3 server, reverse proxy, and application framework with routing, middleware (auth, rate limiting, tracing), an async ORM, background jobs, object storage, and a mailer. Runs as a zero-code config-driven proxy or as a library crate. No third-party HTTP dependencies.
| Mode | Setup | Code required |
|---|---|---|
| Static file server | cargo install rust-web-server && rws |
None |
| Config-driven proxy | rws.config.toml with [[route]] / [[upstream]] |
None |
| Library crate | cargo add rust-web-server |
Yes |
Quick start — library
# Cargo.toml
[]
= "17"
= { = "1", = ["rt-multi-thread", "macros"] }
use *;
async
See DEVELOPER for 59 use-case examples covering JSON, auth, WebSocket, SSE, middleware, ORM, MCP, and more.
Quick start — static file server
Starts on http://127.0.0.1:7878. Place files in the working directory and open the URL.
With HTTPS + HTTP/2 + HTTP/3
Generate a self-signed cert for local development:
HTTP/2 and HTTP/3 are negotiated automatically — no extra configuration needed. See CONFIGURE for all options.
Quick start — config-driven proxy
Drop rws.config.toml in the working directory and run rws — no code required:
[[]]
= "api"
= ["10.0.0.10:8080", "10.0.0.11:8080"]
[]
= "/healthz"
= 10
= 2000
= 2
= 3
[[]]
[]
= "api.example.com"
= "/v1/*"
[]
= "proxy"
= "api"
[]
= { = 500, = 60 }
= { = "bearer", = "API_TOKEN" }
[[]]
[]
= "/*"
[]
= "respond"
= 404
= "Not Found"
See spec/PROXY_SERVER_CONFIG.md for the full annotated config reference.
Building apps with AI
rws is written to be easy for AI coding assistants (Claude Code, Cursor, GitHub Copilot, ChatGPT, etc.) to generate correct code against on the first try — one consistent Router/AppWithState routing pattern, one Middleware trait for auth/rate-limiting/caching/everything else, and no third-party HTTP dependencies whose APIs a model might confuse with this crate's own.
Three files make that possible — point your AI tool at them before asking it to build something:
- llms.txt — a flat, LLM-optimized reference: every public type, middleware, and feature flag, with a runnable snippet for nearly every capability in the crate. This is the file to paste into a chat or system prompt, or fetch directly:
https://raw.githubusercontent.com/bohdaq/rust-web-server/main/llms.txt. - DEVELOPER.md — 72 numbered, runnable use cases (
## Use Case #N: Title). Ask your assistant to follow the closest-matching use case instead of inventing a pattern from scratch. - CLAUDE.md — architecture, request lifecycle, and coding conventions. Claude Code reads this automatically when working inside this repo.
Example prompt:
I'm building on the rust-web-server (rws) crate. Read llms.txt at
https://raw.githubusercontent.com/bohdaq/rust-web-server/main/llms.txt
for the API surface, then build a REST API with:
- GET/POST /todos backed by SQLite (model-sqlite feature)
- JWT auth on POST (auth feature)
- Per-IP rate limiting on every route
Building an AI-powered backend rather than using AI to build the backend? See AI & MCP below — McpServer turns your app into a tool Claude, Cursor, and other MCP clients can call directly.
What's in the box
Protocol & transport
- HTTP/3 over QUIC (UDP) + HTTP/2 + HTTP/1.1 on the same port via ALPN
- TLS via rustls — aws-lc-rs crypto, no OpenSSL
- Automatic TLS (ACME) — Let's Encrypt provisioning + background renewal (
acmefeature) - mTLS — set
RWS_CONFIG_TLS_CLIENT_CA_FILE; client cert required on HTTPS and QUIC - Virtual hosting / SNI — per-domain TLS certs;
Router::with_host()for per-host routing - WebSocket (RFC 6455) — handshake, frame codec, SHA-1 + base64 built in, no extra dep
- Server-Sent Events —
Ssebuilder with correct headers; ideal for AI token streaming - Outbound HTTP client —
Client(sync) andAsyncClient(async,http2feature); HTTPS via rustls
Routing & app building
routes!macro +App::with_state(S)— typed shared state (Arc<S>) across handlersRouterwith:param/*wildcardpath matching;PathParams::get("name")- Async handlers via
App::with_async_state(S)(http2feature) - Middleware pipeline —
app.wrap(layer)stacks composableMiddlewarelayers - Typed extractors —
Body,BodyText,Query,RequestHeaders;#[derive(FromRequest)] - Request validation —
#[derive(Validate)]withlength,range,email,url; returns422 - Typed errors —
AppErrorenum (400–500);IntoResponsetrait for custom error types - Cookie jar —
CookieJarparses;SetCookiebuilder writes all RFC 6265 attributes - Sessions —
SessionStorein-memory TTL sessions;DbSessionStorepersistent sessions backed by the model layer (survives restarts, multi-instance);RedisSessionStoreRedis-backed sessions with automatic TTL expiry; cookie helpers included - JSON —
Json<T>extractor + responder viaserde_json(serdefeature) - HTML templates — Tera engine (Jinja2 syntax);
template::render()one-liner (terafeature) - Dependency injection —
Containerkeyed byTypeId; concrete types anddyn Trait - In-process test client —
TestClient::new(app)dispatches without a TCP socket - Per-instance typed config —
ServerConfigstruct;App::with_config(config),AppWithState::with_config,AsyncAppWithState::with_config, andConfigDrivenApp::with_configall pin an app to explicit settings for parallel-safe integration tests without env-var writes - OpenAPI / Swagger docs —
.openapi(OpenApiConfig)generatesGET /openapi.json+GET /docs(Swagger UI) from registered routes;openapifeature - Per-route timeouts —
with_timeout/with_timeout_state/with_timeout_asyncwrap a handler with its own deadline;TimeoutLayer+ config-driven proxy'stimeout_ms - Request ID middleware —
RequestIdLayerinjects/echoesX-Request-Idon every request and response;RequestIdextractor to read it
Proxy & gateway
- Config-driven proxy —
rws.config.tomlwith[[route]]/[[upstream]]; per-route middleware including bearer/JWT/Basic auth (authfeature for JWT/Basic — no Rust code needed) - Reverse proxy middleware —
ReverseProxy; round-robin;502when all backends fail; built-inConnPoolreuses keep-alive TCP streams; SSE, chunked AI streams, and large downloads are streamed without buffering viaResponse::stream_pipe - HTTP/2 reverse proxy —
H2ReverseProxy(h2://,h2s://,https://);GrpcProxywraps it forContent-Type: application/grpc*(grpc://,grpcs://); TLS upstreams via rustls + ALPNh2 - L4 TCP proxy —
TcpProxybidirectional relay, any TCP protocol (databases, legacy HTTP) - UDP proxy —
UdpProxydatagram proxy; DNS / syslog style - WebSocket proxy —
WsProxyperforms the HTTP upgrade and relays frames bidirectionally;wss://backends connect over TLS via rustls - Health checks — per-upstream background checker; live backend list via
Arc<RwLock<Vec<String>>> - Canary / traffic splitting —
CanaryLayerdistributes requests by weight, lock-free - Circuit breaker — Closed → Open → HalfOpen;
RetryLayerretries on 502/503/504 - Service discovery —
Static,EnvPrefix,File,Dnssources; background refresh thread - Kubernetes Ingress —
KubernetesIngressWatcherpolls K8s API; routes to cluster services
Security
- Per-IP rate limiting — sliding-window
RateLimiter+RateLimitLayer; hot-reloadable - Max request body size —
RWS_CONFIG_MAX_BODY_SIZE_IN_BYTESrejects oversized bodies with413before buffering them, across HTTP/1.1, HTTP/2, and HTTP/3;0(default) is unlimited - CORS — configurable origins, methods, headers; updated live via
SIGHUP - Auth —
BasicAuthLayer(HTTP Basic),JwtLayer(HS256 Bearer),ForwardAuthLayer(delegate to an external auth service, Traefik/nginxauth_requeststyle) (authfeature);JwtLayer::rs256/::es256(RS256/ES256 against a static public key, no JWKS needed) (auth-asymmetricfeature) - IP filter —
IpFilter::allow([...])/deny([...]); exact IPv4 and CIDR ranges - CSRF — double-submit cookie,
SameSite=Strict, constant-time compare (csrffeature) - Password hashing — Argon2id + CSPRNG token generation (
cryptofeature) - Signed and encrypted cookies —
signed_cookie(HMAC-SHA256, tamper-evident) andencrypted_cookie(AES-256-GCM, confidential) (cryptofeature) - OAuth2 / OIDC SSO — authorization-code + PKCE flow; RS256/ES256 JWT via JWKS;
OidcAuthmiddleware; presets for Google, Microsoft, GitHub, Okta, Auth0, Keycloak;from_env();ssofeature - Webhook signature verification —
verify_webhook_signaturefor GitHub (X-Hub-Signature-256), Shopify (X-Shopify-Hmac-Sha256), and Stripe (Stripe-Signature, with replay-window tolerance) (webhookfeature) - Request / response rewriting —
RewriteLayerrewrites headers, URI, status, body bytes;.request_uri_regex_rewrite()for nginx-style regex URI rewrites with capture-group expansion (rewrite-regexfeature)
Observability & ops
- Prometheus metrics —
GET /metrics;MetricsLayeradds per-route counters + histograms - OpenTelemetry tracing —
OtelLayer; W3Ctraceparent; stdout or OTLP (Jaeger, Tempo) - Access log — Combined Log Format or
RWS_CONFIG_LOG_FORMAT=json - Hot config reload —
SIGHUPorPOST /admin/config/reload; no restart required - Graceful shutdown — SIGTERM drains connections;
/readyzreturns503during drain - Background scheduler — fixed-rate, fixed-delay, 6-field cron; one thread per task
- Background job queue —
JobQueue(in-memory) orPersistentJobQueue(crash-safe, model-backed); retry with exponential backoff;jobsfeature - Kubernetes-ready —
/healthz,/readyz,/metrics;0.0.0.0default bind; Dockerfile included - Compression — automatic gzip for text types; chunked streaming for files > 8 MB
AI & MCP
- MCP server —
McpServerserves tools, resources, and prompts over MCP Streamable HTTP (POST /mcp); bearer token auth; connects to Claude, Cursor, and other MCP clients - 8 built-in rws tools —
server_config,feature_flags,server_metrics,rate_limit_config,check_rate_limit,cors_config,list_static_files,reload_config - SSE streaming —
Ssebuilder makes forwarding AI token streams to the browser trivial - Response caching —
CacheLayerTTL cache; vary-by-header;Cache-Controlopt-out
Database / ORM
#[derive(Model)]— maps structs to tables; asyncRepository<T, i64>for zero-boilerplate CRUD (all methods.await)QueryBuilder<T>—.where_eq(),.order_by(),.limit(),.fetch_all().await,.count().await- Pagination —
.paginate(page, per_page)→Page<T>(withtotal_pages) or.paginate_after(cursor, per_page)→CursorPage<T>(keyset, for large tables); both build an RFC 8288Linkresponse header - Migrations —
pool.migrate("migrations/").awaitruns*.sqlfiles in lexicographic order, idempotent - Relations —
HasMany<T>,HasOne<O>,BelongsTo<O>; explicit async load, no hidden N+1 - Backends — SQLite (
model-sqlite), PostgreSQL (model-postgres), MySQL (model-mysql); all implyhttp2(tokio runtime) - Backed by
sqlx— async-native driver;DbPoolis a cheap-to-cloneArc-wrappedsqlx::Pool - In-memory SQLite —
DbPool::memory().awaitfor isolated per-test databases
File / object storage
Storagetrait —put/get/delete/url; write handler code once, swap backendsLocalStorage— stores files on local disk;storage-localfeature, no new depsS3Storage— AWS S3, Cloudflare R2, MinIO via AWS Signature V4 over the outbound HTTP client; no AWS SDK;storage-s3feature- Workload identity — EKS IRSA, ECS task roles, EC2 IMDSv2 auto-detected when static keys aren't set; no static keys required in cloud deployments
Optional features
| Feature | What it adds |
|---|---|
http-client |
HTTPS for outbound Client — adds rustls + webpki-roots |
serde |
Json<T> extractor and responder via serde_json |
auth |
BasicAuthLayer (HTTP Basic, plus from_htpasswd_file), JwtLayer (HS256), and ForwardAuthLayer (delegates auth decisions to an external HTTP service); also wires type = "jwt"/"basic" in the config-driven proxy's [route.middleware.auth] |
auth-asymmetric |
JwtLayer::rs256/::es256 — verify RS256/ES256 JWTs against a static RSA/P-256 public key (PEM), no JWKS endpoint or full sso feature required; implies auth |
macros |
#[get], #[post], …, #[derive(FromRequest)], #[derive(Validate)], #[derive(Config)] |
acme |
Automatic TLS via Let's Encrypt (ACME RFC 8555); implies http2 |
tera |
Tera HTML template engine (Jinja2/Django syntax) |
model-sqlite |
Async ORM backed by SQLite (via sqlx); implies http2 |
model-postgres |
Async ORM backed by PostgreSQL (via sqlx); implies http2 |
model-mysql |
Async ORM backed by MySQL (via sqlx); implies http2 |
crypto |
Argon2id password hashing + CSPRNG token generation; signed_cookie/verify_signed_cookie (HMAC-SHA256) and encrypted_cookie/decrypt_cookie (AES-256-GCM) in cookie |
csrf |
Double-submit cookie CSRF protection |
sso |
OAuth2/OIDC SSO — OidcAuth middleware, RS256/ES256 JWT via JWKS, PKCE, provider presets (Google · Microsoft · GitHub · Okta · Auth0 · Keycloak) |
mailer |
SMTP email — Mailer::from_env() + Email::builder(); plain, STARTTLS, and SMTPS; multipart text+HTML; AUTH PLAIN; no third-party mail library (STARTTLS/SMTPS additionally require http-client or http2) |
jobs |
JobQueue — in-memory background job queue with retry + exponential backoff. PersistentJobQueue (additionally requires a model-* feature) persists jobs to survive a crash/restart. |
storage-local |
LocalStorage — file storage on local disk; no new deps |
storage-s3 |
S3Storage — S3-compatible object storage (AWS S3, R2, MinIO); AWS SigV4 signing via hmac + sha2; static keys or auto-detected workload identity (EKS IRSA / ECS task role / EC2 IMDSv2), no AWS SDK |
openapi |
AppWithState/AsyncAppWithState::openapi(config) — generates GET /openapi.json + GET /docs (Swagger UI) from registered routes; no new deps |
webhook |
verify_webhook_signature — HMAC signature verification for GitHub, Shopify, and Stripe webhooks; hmac + sha2, no new deps beyond what auth/crypto already use |
rewrite-regex |
RewriteLayer::request_uri_regex_rewrite — regex URI rewriting with capture-group expansion, nginx rewrite-directive style; adds regex |
[]
= { = "17", = ["serde", "auth", "macros"] }
Build from source
| Build | Flags | Approx. size |
|---|---|---|
| HTTP/3 + HTTP/2 + HTTP/1.1 + TLS | (default) | ~12 MB |
| HTTP/2 + HTTP/1.1 + TLS | --no-default-features --features http2 |
~8 MB |
| HTTP/1.1 only, no TLS | --no-default-features --features http1 |
~3 MB |
Binary is at target/release/rws. MSRV is 1.75.
Further reading
- CONFIGURE — all configuration options (env vars, config file, CLI flags)
- DEVELOPER — building blocks reference and 72 use-case examples
- FAQ — common problems and solutions
- spec/PROXY_SERVER_CONFIG.md — annotated proxy config reference
- spec/AI_ADOPTION.md — AI adoption strategy
- docs.rs/rust-web-server — API reference
License
MIT