Skip to content

Header Rules

A header rule edits HTTP headers at the edge: on the request before nsin forwards it to your server, on the response before it reaches the visitor, or both from the same rule. Nothing on your server has to change.

Use one when you want a header your application doesn’t send — a Content-Security-Policy, an X-Frame-Options, a shared secret that proves the request came through nsin — or when you want a header your application does send to go away before anyone sees it.

AppliedYour server sees itThe visitor sees it
RequestBefore nsin calls your serverYesNo
ResponseBefore the answer leaves the edgeNoYes

One rule can carry operations in both directions.

Response edits happen after the cache, not before it. That means a cached page gets the headers your rule says today, and editing a rule shows up on the very next request — you never have to purge the cache to change a response header.

A rule is an ordered list of operations. Each one names a direction, an action, a header and (for the first two) a value.

ActionWhat it does
SetReplace whatever is there with your value. Use this when you want exactly one value.
AddAppend your value, keeping anything the client or your server already sent.
RemoveDelete the header entirely. Takes no value.

Operations run in the order you list them, so a later one wins over an earlier one on the same header name.

A remove operation may end in *: X-Debug-* deletes every header starting with X-Debug-. This is the only place a wildcard is allowed — set and add need one exact name to write to.

The reserved list below is re-checked against every header a wildcard actually matches, so a broad pattern can never strip a header nsin owns.

This is the one rule type where every matching rule runs. Everywhere else in nsin the first matching rule wins and the rest are skipped.

Headers work better that way: a zone-wide “my security headers” rule plus a small rule that adds one more header on /admin is the normal shape, and first-match-wins would force you to copy the zone-wide set into every path rule.

When two rules write the same header name, the one lower in the list wins — it runs last. Drag the list to change that.

Like every rule, a header rule can be narrowed by records, hostname and path — see Rules Overview.

Two limits are specific to this type:

  • Visitor IP scope is only available on response-only rules. A request header changes what nsin fetches from your server without changing what the cache stores it under, so the copy fetched for one visitor’s IP would be served to everyone else. Response edits happen per request, after the cache, so they keep the full IP scope.
  • Gateway records are not eligible. One gateway hostname fronts many customers’ upstreams, so a rule that stamps a credential header there would reach somebody else’s traffic.

A header rule can be saved in dry run. The rule still matches and is still recorded, but the request and the response both pass through untouched. Use it to confirm a broad rule is catching what you meant before it changes anything.

LimitValue
Operations per rule16
Length of one value4 KB
Total added per rule8 KB (name + value of every set/add)

4 KB is room for a real Content-Security-Policy while staying under the 8 KB per-header limit that nginx and Apache default to. All three are checked when you save, so an oversized rule fails with a clear message instead of turning into a 431 from your own server later.

Header rules compose — every rule that matches applies, not just the first — so the total a request actually carries is the sum over all matching rules. The edge caps that composed total at 8 KB per direction as well, and drops the ops that would go over it, so a stack of rules can never turn into a 431 either. Removals never count toward the cap and are never dropped.

nsin refuses a small set of names — not to be awkward, but because the edge is already the thing that computes them, and a hand-written value would either be ignored or would break something quietly. The check is per direction and per action, so a name can be refused for one action and allowed for another.

Every refusal comes back as a 400 naming the header and the reason.

On the request:

HeaderWhy
HostIt isn’t an ordinary header at the edge — it also picks the TLS name and the cache slot. Override it on the record, or on an origin route or pool, where it is validated against your zone.
Content-LengthRecomputed from the bytes actually forwarded, and read as a signal for whether a body can be buffered. A wrong value can store a truncated page.
Transfer-Encoding, Connection, Upgrade, Keep-Alive, Proxy-Connection, TE, TrailerHop-by-hop: they describe the connection between the visitor and the edge, and never travel further anyway.
X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port, X-Real-IP, True-Client-IP, Forwardednsin fills these from the connection it actually accepted. A fixed value is a way to hand your server a fake visitor address.
CDN-LoopThe loop counter other CDNs read. Forwarding a foreign marker to a CDN-fronted origin is a confirmed way to earn a 502.
Cache-Control, If-None-Match, If-Modified-Since, If-Match, If-Range, Range, Accept-EncodingThe edge rewrites this set differently on each of its fetch paths, so one rule would behave three different ways.
Sec-WebSocket-*Supplying a handshake key by rule would defeat the tunnel detection that reads it.
Nsn-*, X-Nsin-*, X-Mafar-*nsin’s own namespace. Origins trust these, so a rule that could write one is a forgery.

On the response:

HeaderWhy
Content-LengthRecomputed from the bytes actually written.
Transfer-Encoding, Connection, Upgrade, Keep-AliveHop-by-hop again.
Content-EncodingOwned by the edge’s compressor. What’s stored is always uncompressed, and a stored value is never replayed.
VaryThe cache doesn’t key on it, so declaring a variance nsin won’t honour is a way to serve one visitor’s page to another.
ServerStamped last, deliberately, as the final change before the response goes out.
Strict-Transport-SecurityHSTS can’t be taken back once a browser has seen it, so it belongs to the ramp on Security → HSTS, which raises the duration one step per save. See Security Headers.
Nsn-*nsin’s own analytics and diagnostics surface.

Three request headers are refused for set and add but allowed for remove: Cookie, Authorization and Nsn-Connecting-IP.

The reason they’re blocked is that nsin strips credentials before it refreshes a cached page, and a rule that put one back afterwards would store somebody’s personalised page in the slot everybody shares. A remove can’t do that — it can only agree with the strip.

And stripping is genuinely useful: dropping Cookie before the request reaches your server is how you make a page cacheable that otherwise wouldn’t be, and dropping Nsn-Connecting-IP before a third-party origin keeps your visitors’ addresses to yourself.

Some headers are a switch rather than a text box, because getting them wrong is expensive:

  • X-Content-Type-Options, Referrer-Policy and the fingerprint strip are per-domain toggles — see Security Headers.
  • Strict-Transport-Security and the minimum TLS version live on Security → HSTS for the same reason.
  • The headers nsin always adds to proxied traffic, such as Nsn-Connecting-IP, are listed in Proxy Headers.

Header rules are for your headers. Those pages are for nsin’s.

  • Your own rules. Header rules run last, after the WAF, bot detection, fingerprinting and rewrites. A request header you add is not visible to any of them — deliberately, so a rule can’t be used to slip past your own protection or to trip it.
  • WebSocket upgrades. A 101 handshake doesn’t pass the point where response headers are applied, so response operations don’t affect it. Request operations do.
  • nsin’s own pages. Error pages, challenge pages, the basic-auth prompt and block responses come from the edge, not your server, and your rules don’t touch them.

Security headers for the whole site

  • Response · set · X-Frame-Options · SAMEORIGIN
  • Response · set · X-XSS-Protection · 0
  • Response · set · Permissions-Policy · camera=(), microphone=()

Prove to your server that traffic came through nsin

  • Request · set · X-Edge-Secret · a-long-random-string

Then have your server refuse anything without it — the same idea as only trusting nsin’s proxy headers on traffic that really came from the edge.

Make a marketing page cacheable

  • Path: /landing/*
  • Request · remove · Cookie

Your server stops seeing a session for those pages, so it stops personalising them, so they can be cached for everyone.

Stop leaking your framework

  • Response · remove · X-Powered-By
  • Response · remove · X-AspNet-*

I added a response header but I don’t see it. Check the record is proxied — rules only run for traffic through nsin. If it is, check the rule isn’t in dry run, and remember that error pages and challenge pages come from nsin rather than your server.

Do I have to purge the cache after changing a response header? No. Response headers are applied after the cache, so the change is live on the next request.

Two rules set the same header. Which wins? The one lower in the list — it runs last. See the order of rules.

Can I use the visitor’s country or the ray id as a value? Not yet. Values are fixed text today.

Can a header rule replace my origin’s CORS or CSP setup? It can add or override those headers, and for a static site that’s often simplest. If your application already computes them per response, leave it to the application — an edge rule would flatten every response to one value.