In a Laravel Blade view, why does a value stored as Tom & Jerry show a literal & to visitors, and what does Blade::withoutDoubleEncoding() change?
answer
- the value was escaped twice
- e() second argument, doubleEncode
- default true: & becomes &
- echo format becomes e(%s, false)
- fix the data before the flag
basics
~20 sBlade's e() double-encodes by default, so an entity already in the data becomes & and shows as &. Blade::withoutDoubleEncoding() compiles {{ }} to e($x, false), leaving existing entities intact while still escaping < and >.
solid answer
~40 s`{{ }}` calls `e($value)`, whose second parameter `$doubleEncode` defaults to `true`, so `htmlspecialchars` encodes the `&` of an existing entity: `&` becomes `&amp;` and the visitor sees `&`. The real cause is usually that the value was escaped once already - by an input-sanitizing step before saving, by an import that stored entities, or by an explicit `e()` inside the echo. `Blade::withoutDoubleEncoding()`, called in a service provider's `boot()`, switches Blade's echo format to `e(%s, false)`: existing entities are left as they are, while `<`, `>` and quotes are still encoded. It affects only `{{ }}` echoes compiled after the call, not direct `e()` calls elsewhere, and it makes text that literally contains `<` render as `<`.
code
php · 15 lines<?php
namespace App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// {{ }} now compiles to e($value, false)
Blade::withoutDoubleEncoding();
}
}go deeper
Recall that e() double-encodes by default, so data already containing entities shows them literally on the page.
Explain the doubleEncode argument, the echo format e(%s, false) the switch installs, and its scope: only {{ }} and only newly compiled views.
Trace the symptom to where data was escaped early, fix and migrate it, and weigh the global switch against its fidelity loss.
Hold the line on escape-once-at-output as a team rule, so storage stays raw text and display quirks never push people toward raw echoes.
## The symptom A forum thread title is stored as `Tom & Jerry`. The template prints `{{ $thread->title }}` and the visitor sees `Tom & Jerry` instead of `Tom & Jerry`. Viewing the page source shows `Tom &amp; Jerry`. ## What double encoding means An **HTML entity** is an escape sequence such as `&` (for `&`) or `<` (for `<`). **Double encoding** is escaping text that already contains entities, so the `&` that starts each entity is itself escaped. Laravel's `e($value, $doubleEncode = true)` helper passes that second argument straight to PHP's `htmlspecialchars`. With the default `true`: | Stored value | Output of e() | Visitor sees | |---|---|---| | `Tom & Jerry` | `Tom & Jerry` | `Tom & Jerry` | | `Tom & Jerry` | `Tom &amp; Jerry` | `Tom & Jerry` | | `<b>` | `<b>` | `<b>` | Double encoding is the **faithful** behaviour: whatever characters are in the data, the visitor sees exactly those characters. The page looks wrong only because the data is wrong. ## Where the extra escaping usually comes from 1. **Escaping on input.** A middleware or form hook runs `htmlspecialchars` on request data before saving - the "sanitize on arrival" habit. The database now holds HTML, and Blade escapes it again on output. 2. **Imported content.** A feed, a CMS export or a scraped source stores entities in plain-text fields. 3. **Manual escaping in the view.** `{{ e($title) }}` or `{{ htmlspecialchars($title) }}` escapes twice in one line. 4. **Translation or config strings** written with entities by hand. The durable fix is to stop the first escape: store raw text and escape once, at output. A data migration can unescape already-stored rows. ## What Blade::withoutDoubleEncoding() changes The Blade compiler keeps an **echo format** - by default `e(%s)` - that it wraps around every `{{ }}` expression when compiling a template. `Blade::withoutDoubleEncoding()` sets it to `e(%s, false)`; `Blade::withDoubleEncoding()` sets it to `e(%s, true)`. The docs place the call in `AppServiceProvider::boot()`. After the switch: - Existing entities in the data are left alone, so `Tom & Jerry` renders as `Tom & Jerry`. - `<`, `>`, `"` and `'` are **still encoded**; this is not a way to print HTML, and it does not weaken XSS protection for element text. - Only `{{ }}` echoes use the echo format. `{!! !!}` is unaffected, and direct `e()` calls in PHP classes keep double encoding on. - The setting is baked into compiled views. Templates already compiled with the old format keep it until they are recompiled - when the template file changes or compiled views are cleared. ## The cost of turning it off Disabling double encoding is lossy. A member who writes a post about HTML and types `<` literally now sees `<`, because the entity is passed through and the browser decodes it. On a developer forum that is a visible bug. Because the switch is global, it trades one class of display bug for another. Options, from most to least preferred: - Fix the source of the second escape and migrate existing data. - For a single legacy field, decode once where the data enters the view model, then escape normally. - Use the global switch only when the whole app legitimately stores entity-encoded text and no content needs literal entities. ## Diagnosing it quickly 1. View the page source, not the rendered page: `&amp;` confirms double encoding. 2. Inspect the stored value: an `&` already in the database means escaping happened before output. 3. Search the write path for `htmlspecialchars`, `e(` or a sanitizing middleware applied to request input. 4. Search the view for an `e()` inside `{{ }}`. 5. Only if the data legitimately contains entities, consider the switch. ## Reading it in an interview A good answer names the default (`$doubleEncode = true`), the mechanism (echo format `e(%s, false)`), the scope (only `{{ }}`, only newly compiled views), and above all that the symptom points at data escaped too early, not at Blade misbehaving.
- Does withoutDoubleEncoding make {{ }} unsafe for user input?No. `htmlspecialchars` with `double_encode` off still encodes `<`, `>`, `&` that do not start an entity, and quotes; it only skips re-encoding existing entities. A stored `<script>` renders as visible text `<script>`, not as a tag. The cost is fidelity, not safety: literal entities in user text are displayed decoded.
- You add the call and some pages still show &amp;. Why?The echo format is applied when a template is compiled, and compiled views are cached. Templates compiled before the change keep `e($x)` until the source file changes or the compiled views are cleared. Another cause is an explicit `e()` or `htmlspecialchars` inside the echo, which the switch does not touch.
saying these in an interview costs you the question
- Double encoding is a Blade bug that the switch fixes
- withoutDoubleEncoding lets {{ }} print HTML tags as markup
- The switch also changes e() calls made in PHP classes
- Sanitizing input with htmlspecialchars before saving is good practice
- Switching to {!! !!} is the right fix for a stray &amp;