How to Prevent Cross-Site Scripting (XSS) in Laravel

XSS vulnerabilities allow attackers to inject malicious scripts into your Laravel pages. Learn how to prevent XSS with proper output encoding.

High severity Application Security Updated 2026-03-01

The Problem

Cross-site scripting (XSS) vulnerabilities allow attackers to inject malicious JavaScript into web pages viewed by your users, enabling session hijacking, credential theft, and page defacement. While Laravel's Blade engine escapes output by default with {{ }}, XSS is introduced when developers use unescaped output {!! !!}, render user input in JavaScript contexts, or output data in HTML attributes without proper encoding.

How to Fix

  1. 1

    Always use escaped Blade output

    Use {{ }} (double curly braces) for all user-generated content. This automatically HTML-encodes the output:

    {{ trim($paragraph)); ?>
    {{ trim($paragraph)); ?>

    Only use {!! !!} for content you have explicitly sanitized or that you generated yourself (like rendered Markdown from trusted sources).

  2. 2

    Sanitize HTML when you must allow it

    If you need to allow HTML (rich text editors, Markdown), sanitize it before storage and display:

    {{ trim($paragraph)); ?>

    Then sanitize on input:

    use Mews\Purifier\Facades\Purifier;

    {{ trim($paragraph)); ?>

    Configure allowed tags in config/purifier.php:

    {{ trim($paragraph)); ?>

    Always sanitize on input (before saving) and still use {!! !!} only with sanitized content.

  3. 3

    Handle JavaScript context safely

    Never output user data directly into JavaScript:

    {{ trim($paragraph)); ?>
    {{ trim($paragraph)); ?>

    The @json directive properly escapes data for JavaScript contexts, preventing injection through string breakout.

  4. 4

    Add Content-Security-Policy header

    Add a CSP header to prevent inline script execution, which blocks most XSS attacks even if a vulnerability exists:

    {{ trim($paragraph)); ?>

    With CSP in place, injected scripts cannot execute because the browser blocks inline JavaScript. This provides defense-in-depth alongside proper output encoding.

    See the missing-security-headers guide for full middleware implementation.

How to Verify

Test by entering XSS payloads in your forms:

<script>alert('xss')</script> <img src=x onerror=alert('xss')> <svg onload=alert('xss')>

The text should be displayed literally on the page (you see the HTML tags as text) and never execute as JavaScript. Check the page source to verify the output is HTML-encoded.

Prevention

Establish a coding standard that requires code review approval for any use of {!! !!} in Blade templates. Use @json for JavaScript context output. Add Content-Security-Policy headers. Run automated XSS scanning tools like OWASP ZAP against your application regularly.

Frequently Asked Questions

Does Blade's {{ }} protect against all XSS?

Blade {{ }} protects against XSS in HTML context by encoding <, >, &, ", and '. However, it does not protect when outputting into JavaScript, CSS, or URL contexts. Use @json for JavaScript, and always validate/sanitize URLs before outputting them in href attributes.

Is Livewire vulnerable to XSS?

Livewire uses Blade rendering, so the same rules apply. Properties rendered with {{ }} are safe. If you use wire:model on contenteditable elements or render HTML with {!! !!}, you need to sanitize the content. Livewire's Alpine.js directives should also be checked for user input rendering.

What about XSS in Markdown rendering?

Markdown parsers like league/commonmark can be configured to strip HTML. Use the disallowed_raw_html extension or pipe output through HTMLPurifier. Never trust that Markdown alone prevents XSS, as most parsers allow inline HTML by default.

Detect This Automatically with StackShield

StackShield continuously monitors your Laravel application from the outside and alerts you when security issues are found. No installation required.

Start Free Trial
\n\n{{-- SAFE - use @json which applies JSON encoding --}}\n\n\nThe @json directive properly escapes data for JavaScript contexts, preventing injection through string breakout." }, { "@type": "HowToStep", "position": 4, "name": "Add Content-Security-Policy header", "text": "Add a CSP header to prevent inline script execution, which blocks most XSS attacks even if a vulnerability exists:\n\nContent-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'\n\nWith CSP in place, injected scripts cannot execute because the browser blocks inline JavaScript. This provides defense-in-depth alongside proper output encoding.\n\nSee the missing-security-headers guide for full middleware implementation." } ] }