Middleware & Security

CSP Profiles

Runique provides three built-in profiles, usable via .policy(...) in the builder.

Profile comparison

Directivedefault()strict()permissive()
default-src'none''none''none'
script-src'self' + nonce'self' + nonce'self' + 'unsafe-inline' + 'unsafe-eval'
style-src'self' + nonce¹'self' + nonce'self' + 'unsafe-inline'
img-src'self''self''self' + data: + https:
font-src'self''self''self' + data:
object-src'none''none''self'
media-src'self''self''self' + https:
frame-src'none''none''self'
connect-src'self''self''self'
frame-ancestors'none''none''self'
base-uri'self''self''self'
form-action'self''self''self'
upgrade-insecure-requests
Nonce✅ active✅ active❌ disabled

¹ default() includes 'unsafe-inline' in style-src for htmx compatibility. When the nonce is active, 'unsafe-inline' is dynamically removed and replaced by 'nonce-{val}'.


`SecurityPolicy::default()`

Recommended policy for production. All inline scripts and styles are allowed only via nonce. No external images or fonts.

RuniqueApp::builder(config)
    .middleware(|m| {
        m.with_csp(|c| c)
    })
    .build()
    .await?;

`SecurityPolicy::strict()`

More restrictive than default(): adds upgrade-insecure-requests and enforces the nonce. Use in production for maximum security.

RuniqueApp::builder(config)
    .middleware(|m| {
        m.with_csp(|c| {
            c.policy(SecurityPolicy::strict())
             .with_header_security(true)
        })
    })
    .build()
    .await?;

`SecurityPolicy::permissive()`

Relaxed policy for development or legacy integrations. Do not use in production.

  • 'unsafe-inline' and 'unsafe-eval' enabled → CSP no longer protects against XSS
  • Nonce disabled
  • data: and https: allowed for images and fonts
  • frame-ancestors 'self' instead of 'none'
RuniqueApp::builder(config)
    .middleware(|m| {
        m.with_csp(|c| {
            c.policy(SecurityPolicy::permissive())
        })
    })
    .build()
    .await?;

Custom policy

For a fully custom policy, use the builder methods directly:

RuniqueApp::builder(config)
    .middleware(|m| {
        m.with_csp(|c| {
            c.scripts(vec!["'self'", "https://cdn.example.com"])
             .images(vec!["'self'", "data:"])
             .with_nonce(true)
        })
    })
    .build()
    .await?;

Or build a SecurityPolicy manually for advanced cases:

use runique::middleware::SecurityPolicy;

let policy = SecurityPolicy {
    script_src: vec!["'self'".into(), "https://cdn.example.com".into()],
    img_src: vec!["'self'".into(), "data:".into()],
    ..SecurityPolicy::default()
};

RuniqueApp::builder(config)
    .middleware(|m| {
        m.with_csp(|c| c.policy(policy))
    })
    .build()
    .await?;