Livewire offers a CSP-safe build that allows you to use Livewire applications in environments with strict Content Security Policy (CSP) headers that prohibit 'unsafe-eval'.
Content Security Policy (CSP) is a security standard that helps prevent various types of attacks, including Cross-Site Scripting (XSS) and code injection attacks. CSP works by allowing web developers to control which resources the browser is allowed to load and execute.
One of the most restrictive CSP directives is 'unsafe-eval', which when omitted, prevents JavaScript from executing dynamic code through functions like eval(), new Function(), and similar constructs that compile and execute strings as code at runtime.
By default, Livewire (and its underlying Alpine.js framework) uses new Function() declarations to compile and execute JavaScript expressions from HTML attributes like:
<button wire:click="$set('count', count + 1)">Increment</button>
<div wire:show="user.role === 'admin'">Admin panel</div>While this approach is much faster and safer than using eval() directly, it still violates the 'unsafe-eval' CSP directive that many security-conscious applications enforce.
To enable Livewire's CSP-safe mode, you need to modify your application's configuration:
In your config/livewire.php file, set the csp_safe option to true:
'csp_safe' => true,Important: When you enable CSP-safe mode in Livewire, it also affects all Alpine.js functionality in your application. Alpine will automatically use its CSP-safe evaluator, which means all Alpine expressions throughout your app will be subject to the same parsing limitations.
This is where most developers will notice the constraints, as Alpine expressions tend to be more complex than typical Livewire expressions.
The CSP build supports most common JavaScript expressions you'd use in Livewire:
<!-- These work -->
<button wire:click="increment">+</button>
<button wire:click="decrement">-</button>
<button wire:click="reset">Reset</button>
<button wire:click="save">Save</button>
<input wire:model="name">
<input wire:model.live="search"><!-- These work -->
<button wire:click="updateUser('John', 25)">Update User</button>
<button wire:click="setCount(42)">Set Count</button>
<button wire:click="saveData({ name: 'John', age: 30 })">Save Object</button><!-- These work -->
<input wire:model="user.name">
<input wire:model="settings.theme">
<button wire:click="$set('user.active', true)">Activate</button>
<div wire:show="user.role === 'admin'">Admin Panel</div><!-- These work -->
<div x-data="{ count: 0, name: 'Livewire' }" wire:ignore>
<button x-on:click="count++">Increment</button>
<span x-text="count"></span>
<span x-text="'Hello ' + name"></span>
<div x-show="count > 5">Count is high!</div>
</div>Some advanced JavaScript features won't work in CSP-safe mode:
<!-- L These don't work -->
<button wire:click="items.filter(i => i.active).length">Count Active</button>
<div wire:show="users.some(u => u.role === 'admin')">Has Admin</div>
<button wire:click="(() => console.log('Hi'))()">Complex Function</button><!-- L These don't work -->
<div x-text="`Hello ${name}`">Bad</div>
<div x-data="{ ...defaults }">Bad</div>
<button x-on:click="() => doSomething()">Bad</button><!-- L These don't work -->
<div wire:show="user[dynamicProperty]">Bad</div>
<button wire:click="this[methodName]()">Bad</button>For complex Alpine expressions, use Alpine.data() or move logic to methods:
<!-- Instead of complex inline expressions -->
<div x-data="users">
<div x-show="hasActiveAdmins">Admin panel available</div>
<span x-text="activeUserCount">0</span>
</div>
<script nonce="[nonce]">
Alpine.data('users', () => ({
users: ...,
get hasActiveAdmins() {
return this.users.filter(u => u.active && u.role === 'admin').length > 0;
},
get activeUserCount() {
return this.users.filter(u => u.active).length;
}
}));
</script>Here's an example of CSP headers that work with Livewire's CSP-safe build:
Content-Security-Policy: default-src 'self';
script-src 'nonce-[random]' 'strict-dynamic';
style-src 'self' 'unsafe-inline';The key points:
'unsafe-eval' from your script-src directive'nonce-[random]''strict-dynamic' for better compatibility with dynamically loaded scriptsThe CSP-safe build uses a different expression evaluator that:
For most applications, these differences are imperceptible, but it's worth testing with your specific use case.
To verify your CSP setup is working:
unsafe-eval violations should appearConsider using CSP-safe mode when:
'unsafe-eval'