Security Tip: Don't Use nl2br()!
[Tip#67] As useful as it sounds, nl2br() can potentially leave you open to Cross-Site Scripting (XSS) vulnerabilities... you should reach for CSS instead!

PHP is full of interesting and useful functions for a variety of use cases, and one such function which gets used a lot is nl2br()
!
If you’re not familiar, nl2br()
adds <br>
characters into strings where there are newline characters (\n
, \n\r
, and \r
).
For example:
> nl2br("One\nTwo\nThree");
= """
One<br />\n
Two<br />\n
Three
"""
The most common use case I see for nl2br()
is to display user-submitted inputs from <textarea>
fields. It translates the newline characters the user inputted into actual newlines (via <br>
tags), which are displayed on the page.
However, the risk is that by using nl2br()
, you’re needing to output the user input unescaped on the page, which can introduce Cross-Site Scripting (XSS) vulnerabilities.
For example, if we use the following user input in next few examples:
One
Two
<img src=x onerror="alert('Boom!')">
Three
You can’t use use nl2br()
inside blade escaping tags:
{{ nl2br($input) }}
As it’ll escape the <br>
tags too:

But when you unescape the output:
{!! nl2br($input) !!}
You get this:

One way to work around this is to escape inside the nl2br()
:
{!! nl2br(e($input)) !!}

It works, but it looks pretty ugly and is very easy to forget!
A much better way to solve it is to use the CSS rule white-space: pre-line;
on the escaped input, without making any modifications to the input:
<div style="white-space: pre-line;">{{ $input }}</div>
Or if you use Tailwind CSS:
<div class="whitespace-pre-line">{{ $input }}</div>

This approach provides an incredibly clean solution that takes advantage of standard output escaping to prevent XSS from sneaking in, while still preserving the inputted newlines in the way the user intended. Also, you’re unlikely to forget to escape the output as {{ ... }}
should be your default already.
If you found this security tip useful? 👍
Subscribe now to get weekly Security Tips straight to your inbox, filled with practical, actionable advice to help you build safer apps.
Want to learn more? 🤓
Upgrade to a Premium Subscription for exclusive monthly In Depth articles, or support my work with a one-off tip or recurring Sponsorship! Your support directly funds my security work in the Laravel community. 🥰
Need a second set of eyes on your code?
Book in a Laravel Security Audit and Penetration Test today! I also offer budget-friendly Security Reviews too.
Finally, connect with me on Bluesky, or other socials, and check out Practical Laravel Security, my interactive course designed to boost your Laravel security skills.