
Case study
Dynamic Security Header Enforcement at the Edge – a Native NGINX Module
Plexteq designed and built a native NGINX module in C for Templarbit, a US web-application security platform, that enforces centrally managed HTTP security policies - Content-Security-Policy, HSTS and related headers on live proxied traffic. The module pulls policy updates asynchronously from the Templarbit API and propagates them across every NGINX worker through a versioned shared-memory design, so security teams change policy from a dashboard and see it applied fleet-wide within seconds - with no application changes and no NGINX restarts.
Project Highlights
Industry
Cybersecurity · Web Application Security (SaaS)
Market
United States
Cooperation
2017 – 2018
Expertise
C Systems Programming, NGINX Module Development, Application Security, POSIX IPC
Technologies
C, NGINX Module API, POSIX Shared Memory, slab allocator, rwlock/shmtx, OpenSSL, async DNS/HTTP
Business Challenge
Security policy that changes centrally, but must be enforced at the edge.
Templarbit's product gives security teams a single place to define and monitor the HTTP security posture of their web properties. Rather than hard-coding a Content-Security-Policy, HSTS directive, or frame-ancestor rule into every application and every deployment, a team defines policy centrally, watches CSP violation telemetry, and tightens or relaxes rules from a dashboard. The value of that model depends entirely on one thing: the policy defined in the dashboard has to be the policy that actually reaches the browser - reliably, quickly, and everywhere.
For most of Templarbit's customers, the natural enforcement point is the reverse proxy - and in the overwhelming majority of cases that reverse proxy is NGINX. The challenge is that NGINX was not designed to change response headers dynamically at runtime. The conventional options all fall short of the product's requirements: static add_header directives are fixed at configuration time and require a full process reload; a reload is disruptive at scale and offers no atomic, versioned notion of "the policy is now X"; embedding a scripting runtime adds per-request overhead and an operational dependency; and none of these approaches provide per-resource resolution, API-driven updates, config versioning, or consistent propagation across NGINX's multiple worker processes.
Templarbit engaged Plexteq to build the enforcement layer as a first-class NGINX module in C - one that could pull policy from their API without ever blocking request processing, apply the right headers per resource on all proxied traffic, and switch the whole worker pool to a new policy version atomically, with versioning and safe rollback built in.
Key Challenges
#1. Dynamic policy, no reloads
Headers had to change at runtime - per host, URI, and content type - without editing config files, without reload, and without restarting NGINX, so a dashboard change takes effect on live traffic.
#2. Non-blocking API sync
Polling the Templarbit API for policy updates could not block NGINX's event loop or add latency to any request. DNS resolution, TLS, and HTTP all had to run fully asynchronously inside the worker.
#3. Atomic multi-worker propagation
NGINX runs many independent worker processes. Every worker had to switch to a new policy version consistently - no worker serving stale policy, no request ever observing a half-updated configuration.
#4. Microsecond overhead
The module sits in the response path of every proxied request at production traffic volumes. Header resolution and injection had to cost microseconds and allocate nothing on the hot path.
Solution Delivered
Plexteq delivered the enforcement layer as a native NGINX module written in C. The design cleanly separates a control plane, which synchronizes policy from the Templarbit API - from a data plane, which resolves and injects headers on every response. The two communicate only through a versioned shared-memory region, which is what makes updates both atomic and cheap.
↳ Native NGINX module in C
Rather than scripts or configuration workarounds, the enforcement layer was built as a proper NGINX module in C - part of NGINX itself, with no separate service or runtime to operate alongside it. It hooks into the point where NGINX finalises a response's headers, and it can be delivered in whichever form an operator prefers: loaded as a plug-in at startup or compiled directly into NGINX.
↳ Policy kept in sync, off the request path
Each protected property has a dedicated background thread that periodically fetches its current policy from the Templarbit API. Because this runs entirely separately from live traffic, a slow or unavailable API can never delay a visitor's request. The module only acts when the policy has actually changed — an identical update is fetched and quietly discarded.
↳ A consistent rollout to every worker
When the policy changes, the shared copy is updated under a lock and its version is bumped, so no worker can ever read a half-written policy. Each worker notices the new version on its next request and refreshes its own copy once. In normal running a request only checks a version number - no locking or extra work on the fast path, so protection stays effectively free even under heavy load.
↳ A full automated test suite
The module ships with an automated test suite that exercises its behaviour against a stand-in for the Templarbit API - including the awkward cases: a failing API, malformed responses, and timeouts. This let the security-critical logic be verified in isolation and kept regressions from reaching production.
↳ Documentation and cross-platform builds
Everything was delivered ready to adopt: clear documentation with a worked configuration example, thoroughly commented code, and build scripts that install dependencies and compile the module on both major Linux families - Debian/Ubuntu and RHEL/CentOS/Fedora, with every build checked automatically in continuous integration.
Key Results
<10µs
added per-request overhead for header resolution and injection
Zero
application code changes and NGINX restarts to update policy
<1%
throughput impact under sustained production load
1
pointer swap propagates a new version across the whole worker pool
Business Outcome
The module turned Templarbit's central policy model into something enforceable in production. A security engineer changes a Content-Security-Policy or an HSTS directive in the dashboard, and within a single poll interval every NGINX worker on every proxy is serving the new policy - no configuration edit, no reload, no restart, no application deployment. Because propagation is a single atomic pointer swap behind a shared-memory write lock, there is no window in which some responses carry the old policy and others the new one, and no request ever observes a partially applied configuration.
Separating the asynchronous control plane from the data plane meant that pulling updates from the API - DNS, TLS, HTTP, JSON parsing - never contended with request handling, and conditional ETag fetches kept steady-state synchronization essentially free. On the data plane, per-resource resolution over a prebuilt structure with no hot-path allocation kept the cost of enforcement in the microsecond range even at full traffic. Validation and last-known-good rollback made the whole mechanism safe to operate: a malformed policy is rejected rather than propagated, so central control never becomes a central point of failure.
What the engagement delivered:
-
Runtime policy updates - CSP, HSTS and related headers change on live traffic with no reload, restart, or config edit.
-
Atomic fleet-wide propagation - one shared-memory pointer swap under a write lock moves every worker to the new version with no torn reads.
-
Fully asynchronous control plane - async DNS/TLS/HTTP on an event-loop timer with ETag conditional fetches, never blocking a request.
-
Per-resource enforcement - headers resolved by host, URI, and content type across all proxied traffic.
-
Reload-safe shared-memory store - versioned, slab-allocated policy that survives nginx -s reload.
-
Safe by construction - validated updates and automatic last-known-good rollback prevent a bad push from breaking enforcement.
-
Negligible overhead - microsecond-scale added latency and sub-1% throughput impact at production volume.