navree

CORS and security headers

Allow browser front ends on other origins with exact-origin CORS, and the security headers Navree sends on every response.

On this page

Browsers block a page from reading responses from another origin unless the server allows it with CORS headers. Navree's CORS support is off until you list the origins of your front ends, and then allows exactly those, with credentials. Separately, every response carries a set of security headers that tell browsers not to sniff content types, frame the response or leak the referrer.

Both are part of HttpModule and configured in the http section.

CORS

Allowing origins

List the exact origins (scheme, host and port) of the front ends that call your API from the browser:

config/app.toml
.env

With the list empty, the default, Navree sends no CORS headers at all and browsers refuse cross-origin calls. Each entry must be an origin starting with http:// or https://; * and malformed entries fail the boot:

Text

What Navree answers

For an allowed origin, a preflight request gets:

HTTP

and every actual response, errors included, gets:

HTTP
  • Origins are matched exactly. The response names the caller's origin, never *.
  • Credentials (cookies, Authorization) are allowed, so fetch(url, { credentials: "include" }) works.
  • Methods: GET, HEAD, POST, PUT, PATCH, DELETE and OPTIONS.
  • Request headers: the thirteen listed above, explicitly, since browsers don't accept a wildcard together with credentials. They cover bearer tokens, JSON bodies, conditional requests, tracing, request ids, the CSRF headers and the Pusher socket id.
  • Exposed headers: X-Request-Id, ETag, Location, Retry-After and Content-Disposition are readable from JavaScript.
  • Preflight caching: browsers may reuse a preflight answer for 600 seconds.

Error responses from every layer carry the CORS headers too, including panics, timeouts, 429s and maintenance-mode 503s, so a front end can always read the error body. Preflight requests are answered before the rate limiter and don't count against it.

Options

KeyDefaultWhat it does
http.cors.allowed_origins[] (CORS off)the exact origins allowed
http.cors.allow_credentialstruesend Access-Control-Allow-Credentials: true
http.cors.max_age_secs600how long browsers may cache a preflight answer

The allowed methods, request headers and exposed headers are fixed: there are no keys to change them in this release.

When you don't need CORS

If your front end calls the API through its own server, as the CRM's Next.js backend-for-frontend does, the browser never makes a cross-origin call and you can leave the list empty. The CRM lists http://localhost:3000 anyway, for tools and direct calls during development.

Security headers

Navree adds these headers to every response:

HeaderValueEffect
X-Content-Type-Optionsnosniffbrowsers trust the Content-Type instead of guessing
X-Frame-OptionsDENYthe response can't be shown in a frame (clickjacking)
Referrer-Policyno-referrerlinks from your responses don't leak their URL
Strict-Transport-Securitymax-age=31536000; includeSubDomainsbrowsers use HTTPS for your domain and its subdomains for a year
Content-Security-Policydefault-src 'none'; frame-ancestors 'none'; base-uri 'none'a response opened in the browser can't load scripts, styles or anything else

The content security policy is strict because an API answers with JSON, not pages. Browsers ignore Strict-Transport-Security on plain HTTP, so it does no harm in development; once your domain is served over HTTPS with this header, browsers refuse plain HTTP to it and its subdomains for a year.

Changing a header for one response

Navree only adds a header when the response doesn't have it yet, so a value you set yourself wins. A route that serves HTML can send its own policy:

src/pages/controller.rs

The OpenAPI docs page at /api/docs does exactly this: it sends a policy that lets its script load.

Turning them off

config/app.toml

This removes all five headers from every response, which makes sense when a proxy in front of the app sets them. There's no key to change a single default for every response; to do that, turn them off and have your proxy send your own set. Error responses keep X-Content-Type-Options: nosniff either way.

Testing

The test client sends Origin set to the first allowed origin on every request, so the CORS headers are there in tests. Check them, or the security headers, with header:

tests/cors.rs

Popular pages

  • Installation
  • Your first app
  • Routing and controllers
  • Validation
  • Authentication
  • Benchmarks