alex-frolov / scalar-symfony
Render modern Scalar API References in Symfony from any OpenAPI document
Package info
github.com/alex-frolov/scalar-symfony
Type:symfony-bundle
pkg:composer/alex-frolov/scalar-symfony
Requires
- php: >=8.2
- symfony/config: ^6.4 || ^7.2 || ^8.0
- symfony/dependency-injection: ^6.4 || ^7.2 || ^8.0
- symfony/framework-bundle: ^6.4 || ^7.2 || ^8.0
- symfony/http-foundation: ^6.4 || ^7.2 || ^8.0
- symfony/http-kernel: ^6.4 || ^7.2 || ^8.0
- symfony/routing: ^6.4 || ^7.2 || ^8.0
- symfony/twig-bundle: ^6.4 || ^7.2 || ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.5 || ^11.5
- symfony/browser-kit: ^6.4 || ^7.2 || ^8.0
- symfony/phpunit-bridge: ^6.4 || ^7.2 || ^8.0
- symfony/security-bundle: ^6.4 || ^7.2 || ^8.0
- symfony/security-core: ^6.4 || ^7.2 || ^8.0
Suggests
- symfony/security-core: Required for the attribute-based access control mode
This package is auto-updated.
Last update: 2026-08-17 11:15:54 UTC
README
Render modern Scalar API References in Symfony from any OpenAPI document.
Scalar is an open-source, beautiful and interactive API reference renderer — a modern replacement for the classic Swagger UI. This bundle does one thing: serve the Scalar reference page from your Symfony app in a single route, pointing at your OpenAPI spec.
No OpenAPI generation included (and none required) — bring your spec from any source:
a static openapi.yaml, swagger-php, NelmioApiDocBundle, API Platform, or an
external URL.
Why this bundle?
| Scenario | NelmioApiDocBundle 5 (+Scalar UI) | scalar-symfony |
|---|---|---|
| I already use Nelmio | ✅ built-in Scalar renderer | ✅ compatible (just point url at your nelmio spec route) |
| I use swagger-php / static spec / API Platform | ❌ would need Nelmio as a dependency | ✅ standalone, no Nelmio required |
| Lightweight docs page, one route | — | ✅ one bundle, one route |
| Symfony 6.4+ | needs upgrade to v5 | ✅ works on 6.4 / 7.2+ / 8.x |
Live demo (dogfooding)
The bundle powers the API reference of the Tender Platform (Symfony 8.1, highload auction platform) — a real OpenAPI document served through a single route, no other documentation tooling involved:
Requirements
- PHP >= 8.2
- Symfony 6.4 / 7.2+ / 8.x (framework-bundle + twig-bundle)
- An OpenAPI document served at a public URL (e.g.
/openapi.yaml)
Installation
composer require alex-frolov/scalar-symfony
Configuration
Create config/packages/scalar_symfony.yaml:
scalar_symfony: # Public URL of your OpenAPI document (required) url: '/openapi.yaml' # Route path (default: /scalar) path: '/scalar' # CDN of the Scalar standalone bundle (default: jsdelivr) cdn: 'https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.65.1' # Well-known options (theme, metadata, integration identifier) configuration: theme: 'default' # default | alternate | none ... metaData: title: 'API Reference' description: 'Public API documentation' # Any other Scalar option, passed through verbatim scalar_options: darkMode: false layout: 'modern' # Access control access_control: mode: public # 'public' | 'attribute' attribute: ~ # e.g. ROLE_ADMIN (required when mode: attribute)
Import the bundle routes (add to config/routes.yaml):
scalar_symfony: resource: '@ScalarSymfonyBundle/config/routes.php'
That's it. Your API reference is now served at /scalar (route name
scalar_symfony_reference).
Access control
public(default) — the reference is available to everyone (recommended for public API docs; matches the default Laravel integration behaviour).attribute— the page requires a security attribute:
scalar_symfony: url: '/openapi.yaml' access_control: mode: attribute attribute: ROLE_ADMIN
The attribute mode requires symfony/security-core
and an enabled security.authorization_checker service (i.e. Symfony Security configured).
Requests without the attribute get a 403.
Misconfiguration is detected at container compile time: enabling attribute without
Symfony Security makes bin/console cache:clear (and any container build) fail with a clear
message instead of surfacing an HTTP 500 on the first request.
Known limitation: only the page is protected, not the spec
The attribute mode protects only the HTML reference page (/scalar). The
OpenAPI document itself (the url you configured, e.g. /openapi.yaml) is fetched
client-side by the Scalar bundle in the user's browser — it is never served
through this bundle and the bundle does not proxy it.
Practical consequences:
- If the document is served at a public URL (the default assumption), it stays
publicly downloadable even when the reference page is behind
attribute. - If the document is protected by its own firewall rules, an authorised user's browser may fail to load it (403/CORS), breaking the reference page.
Treat attribute as protection of the page, not of the spec. If the spec must
stay confidential, protect the document with the same rule (or use a protected
proxy endpoint — planned).
How it works
The bundle serves a small HTML page that loads the Scalar standalone bundle from the
CDN and calls Scalar.createApiReference('#scalar-api-reference', { ... }) with your
configuration and the url of the OpenAPI document. The document itself is loaded
client-side by Scalar — the bundle never parses or proxies it.
Hardening: SRI, CSP and self-hosting
The reference page loads the Scalar standalone bundle from a CDN and runs an
inline Scalar.createApiReference(...) call. If the CDN is compromised (or the
cdn value is replaced), arbitrary JavaScript executes with your origin's
privileges. This section documents what you can do today. Built-in sri /
nonce configuration is a planned improvement (see the project TODO).
Self-hosting (recommended)
Download the standalone bundle once and serve it from your own origin — no third-party request at page load, no CDN trust at all:
# Pin the version and download the original package file (not the jsDelivr # minifier output — its hash is not stable, see SRI below): curl -fLo public/scalar/api-reference.standalone.js \ https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.65.1/dist/browser/standalone.js shasum -a 384 public/scalar/api-reference.standalone.js
# config/packages/scalar_symfony.yaml scalar_symfony: url: '/openapi.yaml' cdn: '/scalar/api-reference.standalone.js'
The file is served by your own web server with no extra dependency.
SRI (Subresource Integrity)
When keeping a third-party CDN, pin the expected content hash so a compromised or swapped file is rejected by the browser.
-
The root jsDelivr URL (
…/@scalar/api-reference@1.65.1) is minified on the fly by jsDelivr — the response carries the warning "Do NOT use SRI with dynamically generated files!". Its hash can change without a package release, so it is not SRI-safe. -
The explicit file URL (
…/dist/browser/standalone.js) is served as-is from the immutable npm tarball. Its SHA-384 for 1.65.1 is:sha384-G6dkutu2k5IYVyNESLoFIpgaHx38IJTZ/HhrwN0fecTle9te75y8Kru3rJEJ0ZJV
The bundle does not render integrity yet. To apply SRI today, override the
template (templates/bundles/ScalarSymfonyBundle/reference.html.twig) and add
the attributes to the CDN script tag:
<script src="{{ cdn }}" integrity="sha384-G6dkutu2k5IYVyNESLoFIpgaHx38IJTZ/HhrwN0fecTle9te75y8Kru3rJEJ0ZJV" crossorigin="anonymous"></script>
crossorigin="anonymous" is required for cross-origin SRI (CORS-enabled
request so the integrity check works).
CSP and nonce
If your app sends a Content-Security-Policy with a restrictive script-src,
the inline Scalar.createApiReference(...) script must be allowlisted —
prefer a per-request nonce over 'unsafe-inline'. A nonce-aware config option
is a planned improvement; until then, override the template and add
nonce="{{ csp_nonce }}" to both <script> tags.
Development
composer install composer test # PHPUnit functional tests composer analyse # PHPStan level max composer fix # PHP-CS-Fixer
For the reproducible Docker validation pipeline from the repository root:
./validate.sh
The pipeline runs the regular checks in a pinned Composer image and then tests PHP 8.2 with Symfony 6.4 and lowest dependencies in a disposable copy. The images can be overridden when validating a security-patched base image:
COMPOSER_IMAGE=composer:2.8.12 \ PHP82_BASE_IMAGE=php:8.2-cli-bookworm \ ./validate.sh
License
MIT — see LICENSE.
Credits
Heavily inspired by scalar/laravel (Scalar OpenAPI References in Laravel) and the Scalar project (open-source API platform, MIT).
