Server security

A Fission server app is still a Rust HTTP service. Fission renders widget routes, drains jobs, signs server actions, manages the route-local browser bridge, and serves generated assets, but you still choose the deployment server, reverse proxy, authentication layer, database, and operational controls.
The safe mental model is simple: Fission protects the framework boundary automatically, and your application protects the product boundary. Fission can reject a forged server action token; it cannot know whether a particular user is allowed to buy a card, edit an order, or open an admin page unless your reducer, service, or surrounding HTTP stack checks that rule.

What Fission protects automatically

Fission server applies a set of default protections around the code it generates and the HTTP requests it handles.
Area
Automatic behavior
Server action signing
Action tokens are signed over the route, target node, typed action id, payload bytes, expiry, and nonce. Tampered tokens are rejected.
Replay protection
verify_once stores recently used action token identities and rejects replay of the same token.
Expiry
Server action tokens are short lived. Expired tokens are rejected before reducer dispatch.
Body size
Server action request bodies larger than MAX_SERVER_ACTION_BODY_BYTES are rejected.
Origin checks
ServerRenderer::with_allowed_action_origin lets the host restrict action posts to known origins.
Private route caching
ServerPrivate routes are not inserted into the public full-page cache.
Revalidated action safety
A revalidated page that renders signed action forms fails the render instead of caching request-specific tokens.
Cache keys
Revalidated page cache keys include normalized query data, app identity, locale, theme fingerprint, build id, and declared vary headers.
Asset paths
Generated asset serving rejects path traversal and serves only project/static/generated asset roots.
Worker DOM policy
Browser workers can mutate only allowed nodes or semantic targets through the generated bridge. Unsafe URLs and inline event attributes are rejected.
Island DOM replacement
Fission widget islands render HTML through the Fission HTML renderer; replacement operations are still checked by the bridge protocol before application.
Session cookies
Session cookies are HttpOnly. When signing_key_env is set, cookie values are signed and tampered cookies are rejected.
These protections are framework-level guard rails. You still need product tests for permission checks, tenant isolation, payment rules, inventory transitions, and any provider-specific API call you make from jobs or services.

Configure sessions deliberately

ServerPrivate routes receive an opaque session id through ctx.session.id(). Use that id as a key into your own server-side store. Do not put user data inside the cookie.
[server.sessions]
cookie_name = "shop_session"
signing_key_env = "SHOP_SESSION_SECRET"
secure = true
same_site = "lax"
In local HTTP development, secure = false is acceptable. In production HTTPS deployments, set secure = true and provide SHOP_SESSION_SECRET through your process manager, container platform, or CI secret store. Use same_site = "strict" for apps that never need cross-site form or identity-provider flows. Use same_site = "none" only when cross-site embedding or auth redirects require it; Fission requires secure = true for that mode.

Restrict action origins

Server action tokens stop forgery and tampering, but origin checks are still useful when the server knows the public origins that may submit actions.
let renderer = ServerRenderer::configured(app)?
    .with_allowed_action_origin("https://cards.example")
    .with_allowed_action_origin("https://www.cards.example");
If no allowed origins are configured, Fission accepts requests without an Origin header because normal same-origin form posts and some command-line tests do not send one. A production host can be stricter by configuring allowed origins and by adding middleware in the HTTP adapter layer.

Use the existing Rust HTTP ecosystem

Fission does not need to build its own middleware ecosystem. The server shell exposes adapters so you can run a ServerRenderer inside the HTTP stack you already use.
Hyper is available with the normal server feature:
[dependencies]
fission = { version = "0.3", features = ["server"] }
Axum and Actix adapters are feature-gated to avoid pulling those dependencies into projects that do not use them:
[dependencies]
fission = { version = "0.3", features = ["server", "server-axum"] }
tower = "0.5"
A small Axum host can wrap the Fission handler with ordinary middleware:
use axum::{routing::any, Router};
use fission::server::{axum_adapter, ServerRenderer};
use std::sync::Arc;
use std::time::Duration;
use tower::limit::RateLimitLayer;

let renderer = Arc::new(ServerRenderer::configured(pokemon_card_store_server())?);
let app = Router::new()
    .fallback(any(axum_adapter::handle))
    .with_state(renderer)
    .layer(RateLimitLayer::new(120, Duration::from_secs(60)));
That same pattern applies to authentication, tracing, request ids, compression, timeout, body-limit, CORS, and provider-specific middleware. Put cross-cutting HTTP policy in the HTTP framework. Put typed application behavior in Fission reducers, jobs, and services.

Use hooks at the right layer

There are three useful hook points.
Hook point
Use it for
Where it lives
HTTP middleware
Rate limits, auth sessions, request logging, CORS, compression, TLS/proxy policy, body limits
Axum, Actix, Hyper, or your chosen Rust HTTP stack
Server renderer builder
Action origin allow-list, cache provider, action signer, viewport defaults, render pass limit
ServerRenderer configuration before serving
App model
Permission checks, tenant checks, cart rules, inventory updates, cache invalidation requests
Fission reducers, jobs, and services
Do not put product authorization only in a button's visibility. Hiding a button improves the UI, but the reducer or service that performs the mutation must still check whether the current session/user is allowed to do it.

Harden cache behavior

Public cache entries must never contain private data. Use ServerPrivate when a route depends on session, account, tenant, cart, or permission state. Use Revalidated only for full pages that are safe to share.
For shared deployments, prefer a cache pipeline:
[server.cache]
provider = "pipeline"

[[server.cache.layers]]
name = "hot"
provider = "moka"
policy = "hot-only"
max_capacity = 2048

[[server.cache.layers]]
name = "shared"
provider = "redis"
policy = "write-through"
url_env = "REDIS_URL"
prefix = "cards"
The route still owns its tags and vary fields. If changing a product should invalidate catalogue pages, tag those pages with catalog or product:<id> and invalidate that tag from the service that applies the product update.

Configure proxy-aware URLs only behind a trusted proxy

Use [server.http].base_url when the public origin is stable:
[server.http]
base_url = "https://cards.example"
If the same binary serves multiple hostnames behind infrastructure that sets forwarded headers, you can enable proxy-derived canonical URLs:
[server.http]
trust_proxy_headers = true
Only do this when your proxy overwrites X-Forwarded-Proto and X-Forwarded-Host. Do not trust forwarded headers from the public internet directly.

Test security paths explicitly

Add tests for the cases attackers and operational mistakes are likely to exercise.
Test
Expected result
Tampered action token
403 and no reducer dispatch
Replayed action token
First request succeeds, second request is rejected
Oversized action body
413
Wrong origin
403 when an origin allow-list is configured
Revalidated page with action form
Render fails before cache write
Private route
No public full-page cache entry is written
Path traversal asset request
400
Worker operation outside policy
Bridge validation rejects the operation
Tampered signed session cookie
New session is created and the tampered value is ignored
The server shell includes framework tests for these behaviors. Your app should add tests for the product-specific checks the framework cannot infer.
Fission
A cross-platform, GPU-accelerated user interface framework for Rust. MIT licensed.
Copyright (c) 2026 Fission
Ready to use today. Widget APIs are expected to remain stable; some runtime and shell APIs may change before 1.0.0.
main - v0.1.0 alpha