Safe Mode
Safe mode is enabled by default to provide XSS protection. This protects against malicious content in user comments, forum posts, or any input from external sources.
Default Behavior
The |djot filter uses safe mode by default. For trusted content (admin/CMS), use |djot_raw or a named converter with safe_mode: false.
{# Safe by default - use for any content #}
{{ content|djot }}
{# Explicit raw - only for trusted content you control #}
{{ article.body|djot_raw }}When to Disable Safe Mode
| Content Source | Filter to Use |
|---|---|
| User comments | ` |
| Forum posts | ` |
| External API data | ` |
| User profile descriptions | ` |
| Admin/editor content | ` |
| CMS content (trusted editors) | ` |
Rule of thumb: Only use |djot_raw when you fully control and trust the content or sanitized the possible HTML content using e.g. HTMLPurifier.
What Safe Mode Does
When enabled (the default), safe mode:
- Sanitizes URLs - blocks
javascript:,data:, and other dangerous protocols - Removes raw HTML - strips any embedded HTML/scripts
- Validates links - ensures URLs are safe
Example: Dangerous Link
Input:
[Click me](javascript:alert('XSS'))With |djot (safe mode, default):
<p><a href="">Click me</a></p>With |djot_raw (no safe mode):
<p><a href="javascript:alert('XSS')">Click me</a></p>Example: Raw HTML
Input:
`<script>alert('XSS')</script>`{=html}With |djot (safe mode): Raw HTML blocks are stripped.
With |djot_raw (no safe mode): Script is rendered.
Using Named Converters
For more control, define named converter profiles:
# config/packages/symfony_djot.yaml
symfony_djot:
converters:
# Default is already safe (safe_mode: true)
default: ~
# For trusted CMS content
trusted:
safe_mode: false
# With extensions for documentation
docs:
safe_mode: false
extensions:
- table_of_contents
- heading_permalinks{# Uses default safe converter #}
{{ comment.text|djot }}
{# Uses trusted converter #}
{{ article.body|djot('trusted') }}
{# Uses docs converter with extensions #}
{{ documentation.content|djot('docs') }}In Services
use PhpCollective\SymfonyDjot\Service\DjotConverterInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
class ContentRenderer
{
public function __construct(
// Default converter (safe mode)
private DjotConverterInterface $default,
// Trusted converter (no safe mode)
#[Autowire(service: 'symfony_djot.converter.trusted')]
private DjotConverterInterface $trusted,
) {}
public function renderComment(Comment $comment): string
{
// User-generated content - use safe default
return $this->default->toHtml($comment->getText());
}
public function renderArticle(Article $article): string
{
// Trusted content from editors
return $this->trusted->toHtml($article->getBody());
}
}Security Recommendations
- Use the default -
|djotis safe by default, use it everywhere - Explicit trust - only use
|djot_rawfor content you control - Validate before storing - safe mode helps at render time, but validate input too
- Review trusted content - even "trusted" content should be reviewed
More Information
For advanced safe mode options (custom blocked schemes, strict mode), see the php-collective/djot safe mode documentation.
Next Steps
- Configuration - set up converter profiles
- Service Usage - use in PHP code