Community

Apps

L402 Nginx

This is infrastructure, not a wallet UI. It sits inside Nginx's request path, issues HTTP 402 challenges, verifies L402 macaroons and payment preimages, and lets operators sell API access per request or per route.

L402 Nginx visual
L402 Nginx icon
Apps The product layer Clients, signers, publishing tools, wallets and useful experiments.
Back to Nostr
Apps

Apps shelf

Apps pages collect clients, signers, tools, developer libraries and product research without turning the app into the whole network.

Apps29 min readNginx module for charging Lightning or Cashu payments before serving protected HTTP and gRPC routes

L402 Nginx

This is infrastructure, not a wallet UI. It sits inside Nginx's request path, issues HTTP 402 challenges, verifies L402 macaroons and payment preimages, and lets operators sell API access per request or per route.

The quick readL402 Nginx is the `ngx_l402` project by Dhananjay Purohit. It is a Rust `cdylib` Nginx module, not a Nostr app and not a wallet. Its job is to protect HTTP and HTTP/2 API routes with L402 payment authentication: an unauthenticated request receives `402 Payment Required` plus a `WWW-Authenticate: L402` challenge containing a macaroon and BOLT11 invoice; after payment, the client retries with `Authorization: L402 :` or, when server-side auto-detect is available, just the macaroon. The project supports Lightning backends including LND, LNC, CLN, Eclair, LNURL, NWC and BOLT12, plus Cashu tokens as an alternate payment header. The Nostr relation is specific: NWC is one possible Lightning backend via `NWC_URI`; the module does not publish social notes, manage relays for user identity, or implement a Nostr client. Repo metadata checked on June 14, 2026 showed `DhananjayPurohit/ngx_l402` as an MIT-licensed Rust repo created on February 1, 2025, pushed on June 13, 2026, with 50 stars, 2 forks and 24 open issues. Latest release was `v1.2.6`, published May 24, 2026. Important checks before using it: Nginx version compatibility, `ROOT_KEY` secrecy, backend credentials, Redis replay protection, invoice rate limits, macaroon expiry, path and method caveats, auto-detect backend support, Cashu mint trust, public manifest exposure, metrics access control and the open issues around per-worker counters and NWC replay protection in test containers.

It is an access gate, not an app

L402 Nginx is best understood as a gate in front of an HTTP service. The actual project name is `ngx_l402`, and the repository describes it as an Nginx module for L402 authentication and Lightning-based API monetization. That is the right mental model: it runs where Nginx decides whether a request should pass through.

This is not a user wallet, not a marketplace, not a Nostr social client and not a hosted payment service. A developer or operator installs the module, chooses a Lightning backend, marks Nginx locations as protected, and then clients must pay before those routes return content.

The route belongs in Developer Tools & Libraries because it is infrastructure for paid APIs. It can be useful to AI agents, data APIs, media services, gRPC endpoints and any server where a small per-request Lightning payment is a better fit than accounts, subscriptions or card checkout.

The Nostr connection is narrower than the name list can imply. `ngx_l402` can use Nostr Wallet Connect as a Lightning backend. That means it can ask a remote NWC wallet service to create invoices or handle Lightning operations. It does not make Nginx a Nostr client for profiles, feeds, direct messages or social publishing.

The L402 flow in plain terms

The default flow is classic L402. A client requests a protected route without payment. The module generates a macaroon, obtains a Lightning invoice from the configured backend, and returns HTTP `402 Payment Required` with a `WWW-Authenticate` header containing both values.

The client pays the invoice. A normal Lightning wallet gives the client a payment preimage. The client retries the same route with an `Authorization` header in the form `L402 :`. The module checks the macaroon, checks that the preimage hashes to the invoice payment hash, checks caveats, and only then lets the request through.

The current source binds macaroons to both normalized request path and HTTP method. That matters because a token bought for `GET /small` should not automatically authorize `POST /expensive`. Release `v1.2.6` explicitly calls out binding L402 macaroons to HTTP method to prevent cross-method replay.

From the reader's side, L402 Nginx turns an API endpoint into a vending machine: request, receive challenge, pay, retry with proof. From the operator's side, it is a hot authentication path with money accounting, secrets, backend calls and failure states inside every protected request.

The project is active and still moving

GitHub metadata checked on June 14, 2026 showed `DhananjayPurohit/ngx_l402` as an MIT-licensed Rust repository created on February 1, 2025. It had 50 stars, 2 forks, 24 open issues, was not archived, and had been pushed on June 13, 2026.

The latest release was `v1.2.6`, published on May 24, 2026. That release added a `/.well-known/l402-services` capability manifest, HTTP-method binding for macaroons, an HTML invoice display page, a five-second bound for auto-detect Lightning lookup, and per-method payment and invoice metrics.

The Cargo manifest reports package version 1.2.6 and builds a Rust `cdylib` named `ngx_l402_lib`. Important dependencies include `ngx`, `l402_middleware`, `tokio`, `reqwest`, `tonic_openssl_lnd`, `cdk`, `cdk-sqlite`, `redis`, `macaroon`, `cln-rpc`, `lightning` and `qrcode-generator`.

That active pace is good, but it also means operators should pin versions and read release notes. The open issue list includes real operational concerns: per-worker metrics counters, NWC container replay protection wiring, environment examples, payment page test coverage and planned MPP-Lightning work.

Installation is tied to Nginx compatibility

The README says the quick-start Docker path is the easiest route, with images published under `ghcr.io/dhananjaypurohit/ngx_l402`. It also states that prebuilt binaries are provided for Nginx 1.28.0 only, and that other Nginx versions should build from source.

The manual build docs require system build tools, Rust, Nginx source matching the target version, `./configure --with-compat`, and a `cargo build --release --features export-modules`. The compiled shared object is then copied into the Nginx modules directory.

That is a normal dynamic-module concern, but it is not a footnote. If the module ABI and deployed Nginx do not match, an operator can fail at load time or worse. A production rollout should test the exact Nginx build, module binary, OS image and deployment method before putting it on a public edge server.

Docker makes first tests easier because it packages the intended combination. Manual installation gives more control, but it also means the operator owns Nginx versioning, source download, module path, service reloads and rollback.

Configuration happens in two layers

The module uses environment variables for global backend and secret settings. The docs list `LN_CLIENT_TYPE` as the required switch, with supported values `LND`, `CLN`, `LNURL`, `NWC`, `BOLT12` and `ECLAIR`. The README also discusses LNC for remote LND access through Lightning Terminal.

The route behavior is controlled inside Nginx `location` blocks. The current Rust command table includes directives such as `l402`, `l402_amount_msat_default`, `l402_macaroon_timeout`, `l402_lnurl_addr`, `l402_invoice_rate_limit`, `l402_auto_detect_payment`, `l402_dry_run`, `l402_metrics`, `l402_manifest`, `l402_manifest_hide` and `l402_indefinite_access`.

That split is practical. Backend credentials and root secrets belong in the process environment or secret manager. Route prices, timeouts, rate limits and exposure rules belong in Nginx config where operators already define locations.

The mistake to avoid is treating one layer as complete. A route with `l402 on` is not safe if `ROOT_KEY` is weak, backend credentials are too broad or Redis replay protection is missing. A perfect backend is not useful if a location has no timeout, no rate limit and an accidental public manifest.

Lightning backends are not identical

The docs list several backend shapes. LND can be reached directly over gRPC with a macaroon and TLS cert. CLN uses the Core Lightning RPC file. Eclair uses its REST API. LNURL delegates invoice generation to a Lightning address or LNURL server. NWC uses a `nostr+walletconnect` URI. BOLT12 uses a reusable offer with CLN RPC access.

For an operator, each backend has a different trust and failure model. Direct node backends keep settlement lookup close to the server but require sensitive node credentials. LNURL and NWC push invoice generation through remote wallet services and reduce local node exposure, but they also reduce what the module can verify directly.

The NWC backend is especially relevant to this hub. The environment docs show `LN_CLIENT_TYPE=NWC` and `NWC_URI=nostr+walletconnect://?relay=&secret=`. That URI is wallet access material. It should be stored like a secret, not pasted into logs or committed to a repo.

Because NWC rides through Nostr relays, operators should check relay reliability, wallet-service uptime, supported NIP-47 methods and permission limits. This module is not the place where NWC permissions become user-friendly. The connected wallet service still controls what that NWC secret can do.

Auto-detect is useful but backend-limited

Classic L402 asks the client to return the preimage. The module also supports server-side auto-detect through `l402_auto_detect_payment on`. In that mode, the client can retry with only the macaroon, and the module queries the Lightning node to see whether the invoice is settled and retrieve the preimage.

The backend support matrix is critical. The docs say auto-detect works for LND, CLN, BOLT12 and Eclair. It does not work for LNC, NWC or LNURL. The source explains the NWC reason: `lookup_invoice` is optional in NIP-47 and not universally supported.

That means NWC deployments should expect the classic preimage flow unless the surrounding wallet setup provides another reliable settlement signal. A server cannot simply ask every NWC wallet service for settled invoice state and expect consistent results.

Auto-detect also changes latency and failure behavior. The payment detector source has node-specific lookup paths, and dry-run challenge synthesis is capped to avoid letting backend slowness harm user-facing traffic. Operators should test unsettled invoices, missing invoices, backend timeouts and repeated retries.

Redis carries more than convenience

Redis is optional in the sense that the module can start without it, but many serious behaviors depend on it. The docs use Redis for dynamic pricing, per-tenant LNURL overrides, replay prevention and invoice rate limiting.

Dynamic pricing is simple: a Redis key matching the request path can override the static `l402_amount_msat_default`. Per-tenant LNURL uses keys such as `lnurl:/api/tenant1` to route different paths to different Lightning addresses. That is useful for multi-tenant APIs, but it makes Redis part of the payment control plane.

Replay protection is the more important part. The module stores hashes of used preimages and Cashu tokens with TTLs. Without persistent shared state, a distributed deployment can accidentally allow the same credential through more than once. The open issue about the NWC container lacking Redis-backed replay protection is a good reminder.

Redis itself must be protected. A public or shared Redis can alter prices, route LNURL payments incorrectly, disable effective rate limits or weaken replay protection. Treat it as payment infrastructure, not as a disposable cache.

Cashu support adds a second payment rail

The module can accept Cashu eCash tokens as an alternative to Lightning invoices. The docs describe two modes. Standard mode calls wallet receive logic and contacts a mint per request. P2PK mode verifies tokens locked to the proxy public key locally and is intended for higher-traffic deployments.

Cashu makes sense where clients can hold eCash and where mint trust is acceptable. It can reduce direct Lightning round trips for each request, but it introduces mint selection, token storage, redemption behavior and wallet seed security.

The configuration docs are direct about secrets. `CASHU_WALLET_SECRET` can control token funds, and `CASHU_P2PK_PRIVATE_KEY` can spend tokens locked to the proxy public key. Both need real secret management. In P2PK mode, whitelisted mints are required for security and for the NUT-24 payment request.

Operators should test Cashu as its own payment rail. Check token replay, mint whitelisting, database persistence, redemption to Lightning, proof count limits, small-balance fee reserves and how failures are communicated to clients.

The manifest makes paid routes discoverable

Release `v1.2.6` added a capability manifest. The `l402_manifest` directive turns a location, typically `/.well-known/l402-services`, into a JSON endpoint describing protected routes, prices, backend types, accepted payment methods and route caveats.

That is important for agent-era use. A client that only knows a host can fetch the manifest, see what routes cost and then follow the challenge-payment-retry flow without an out-of-band integration document.

The manifest is also information disclosure by design. The docs warn that pricing data is public unless operators restrict the endpoint. The `l402_manifest_hide` directive can keep a route out of the manifest while still enforcing L402 on it.

Use the manifest when discoverability matters. Restrict or hide it when route names, pricing, tenant structure or backend choices are sensitive. A paid API map can be valuable to customers and competitors at the same time.

Dry-run mode is the responsible rollout path

The dry-run docs describe shadow mode through `l402_dry_run on`. In this mode, the module evaluates pricing, backend reachability, Authorization headers, challenge generation, logs and Prometheus counters, but always lets the upstream request pass.

That is the right way to introduce payment enforcement to an existing API. You can see what would be challenged, what would be allowed, how often invoice generation fails, which prices are being used and whether rate limits would trigger before customers are actually blocked.

Dry-run still has cost. The docs warn that unauthenticated requests can generate real invoices against the backend unless rate-limited or sampled. A busy public API should start with canary paths, invoice limits and careful metrics rather than turning shadow mode on everywhere.

The current issue list notes that dry-run CI can be flaky because Prometheus counters are per Nginx worker. That is a test and observability warning, not a reason to avoid shadow mode. It is a reason to understand that multi-worker Nginx can split counters unless metrics are designed around shared state.

Metrics are useful and sensitive

The `l402_metrics` directive exposes Prometheus-style counters for requests, challenges, valid payments, Lightning payments, Cashu payments, invalid payments, missing auth, invoice generation, rate-limited requests and dry-run outcomes.

Those counters are operationally valuable. They tell an operator whether the payment gate is healthy, whether clients are paying successfully, whether backend invoice generation is failing, and whether a shadow rollout would block too much traffic.

They can also leak business data. Public metrics can reveal traffic volume, pricing experiments, conversion rate, backend failures and paid route popularity. The docs recommend restricting metrics with Nginx allow/deny, auth subrequests or firewall rules.

The per-worker issue matters here too. Static counters inside Nginx workers may not aggregate the way Prometheus users expect when traffic and scrapes hit different workers. Treat metrics as a production feature to verify, not as a decorative endpoint.

Security lives in the boring settings

`ROOT_KEY` is the root of macaroon signing. The code refuses keys shorter than 32 characters, but length is only the first check. Operators should generate a high-entropy value, store it in a secret manager, rotate it deliberately and understand that rotation can invalidate outstanding macaroons.

Backend credentials are equally sensitive. LND macaroons, TLS certs, CLN RPC paths, Eclair passwords, LNC pairing phrases, NWC URIs, BOLT12 offers tied to node access, Redis URLs and Cashu secrets all sit near money movement or access control.

The security policy is frank: the module runs inside the Nginx worker process, crosses an unsafe FFI boundary and sits on the authentication path of every protected request. A bug can mean bypassed authentication, leaked secrets or crashed workers.

That does not make the project unsafe by default. It means operators should deploy it like serious edge infrastructure: least-privilege backend credentials, private Redis, restricted metrics, careful logs, version pinning, staging tests, rollback and private vulnerability reporting.

The HTML payment page is helpful but not every API wants it

The source includes a payment page renderer that can show a BOLT11 invoice, QR code, preimage input, auto-detect polling and a Cashu tab when Cashu is enabled. That is useful for humans testing a protected route in a browser.

For pure API clients, the `WWW-Authenticate` header is still the primary integration surface. A machine client should be able to ignore the HTML, parse the L402 challenge, pay the invoice and retry with Authorization.

One open issue tracks testing and making HTML rendering more configurable. It asks for payment-page tests and an opt-in or opt-out directive so API deployments can prefer plain challenge behavior. That is a normal maturity point for a module moving from demo-friendly to production-friendly.

Operators should test both browser and API clients. If a paid route is consumed only by agents or services, confirm content type, headers, status codes and retry behavior without relying on the visual page.

What to verify before production

Start with a local or staging route. Confirm that an unpaid request returns `402`, that the `WWW-Authenticate` header contains a usable macaroon and invoice, that payment unlocks the route, and that a wrong preimage, wrong method or wrong path fails.

Then test replay. Reuse the same preimage with Redis enabled and disabled. Run multiple Nginx workers. Restart Nginx. Run more than one Nginx instance behind a load balancer. If the same credential works twice where it should not, fix replay state before going live.

Test every backend separately. LND, CLN, Eclair, LNURL, NWC and BOLT12 do not fail the same way. For NWC, confirm the wallet service's permissions and invoice behavior. For LNURL, confirm remote wallet availability. For direct nodes, confirm credentials are least privilege.

Finally test operations: dry-run metrics, rate limits, Redis outage, backend timeout, secret rotation, manifest exposure, Cashu mint outage, old macaroon expiry and logs. A paid gateway is only good if it fails visibly and recoverably.

Where it fits in the Nostr Wallet Connect map

L402 Nginx appears on the Nostr Wallet Connect ecosystem map because one of its Lightning backends is NWC. That is enough to matter: a server can monetize API requests through a remote wallet connection instead of running a local node backend.

But it should not be sold as a Nostr-native app. The Nostr path is a backend option, not the entire product. The module's core is HTTP 402, macaroons, Lightning invoices, Cashu, Redis and Nginx.

This distinction helps users choose correctly. If you need a wallet service, look at Alby Hub, LNbits, phoenixd, LND, CLN or another backend. If you need an API gateway that charges before serving a route, L402 Nginx is the relevant piece.

In practice, it can sit above several wallet stacks. An operator might start with LNURL for simplicity, move to LND or CLN for stronger auto-detect, add Cashu for eCash clients, and later evaluate NWC where remote wallet control fits the deployment model.

The practical close

L402 Nginx is one of the more concrete attempts to make small internet payments operational instead of theoretical. It does not ask every API developer to design a billing system. It gives Nginx a payment-aware access handler.

Its strength is breadth: multiple Lightning backends, NWC, Cashu, Redis dynamic pricing, replay protection, invoice rate limiting, metrics, dry-run mode and a capability manifest. That is a serious toolkit for paid APIs.

Its risk is also breadth. Every backend adds configuration, credentials and failure modes. Every optional feature adds a production question. NWC does not support the same server-side lookup path as LND or CLN. Cashu changes the trust model. Redis becomes part of the payment boundary.

Use it when you are ready to operate it like infrastructure. Pin the version, protect secrets, restrict observability endpoints, test with real clients, keep dry-run data, and read the open issues before you put important routes behind it. In that posture, L402 Nginx is a useful bridge between web APIs and Lightning-native payment proofs.

Useful Nostr context

These pages connect L402 access control with Lightning addresses, NWC payments and commerce infrastructure.

Sources worth opening

Start with the README, release notes, configuration docs and the Rust source paths for directives, replay protection, payment detection and manifests. Then compare the module behavior with L402, NIP-47, Cashu and Nginx operational docs.

Back to the Crays Nostr page
Apps route visual cue 1
Apps route visual cue 2
Apps route visual cue 3
Apps route visual cue 4
Apps route visual cue 5

How to use this page

Keep the product map close.

Search the wider app shelf when you want another client, tool, protocol reference or source trail beside this page.

AppsBrowse the Apps routeApps pages stay availableProducts, tools, communities and source trails.Browse pages
Apps contribution visual cue 1
Apps contribution visual cue 2
Apps contribution visual cue 3
Apps contribution visual cue 4
Apps contribution visual cue 5

Bring something back

Ask, suggest, submit or nominate.

Ask a question, send a source, suggest a fix, submit a project or nominate a public Nostr account. The page stays stable; your contribution gets reviewed beside it.