duc01nguyen/security-headers-bundle-symfony
Clone the Repository
git clone https://github.com/ndhhaiduong/demo-symfony-package.git
cd demo-symfony-package
Install Dependencies
composer install
Register the Bundle
In your Symfony project’s config/bundles.php, add:
return [
// ...
Duc01nguyen\SecurityHeadersBundle\SecurityHeadersBundle::class => ['all' => true],
];
Configure Headers Publish the default configuration:
php bin/console config:dump-reference Duc01nguyenSecurityHeadersBundle
Then customize in config/packages/duc01nguyen_security_headers.yaml:
duc01nguyen_security_headers:
headers:
Content-Security-Policy: "default-src 'self'"
X-Frame-Options: "DENY"
Verify Headers
Use curl or browser dev tools to check headers:
curl -I http://your-symfony-app.dev
Enforce CSP for a Public API
duc01nguyen_security_headers:
headers:
Content-Security-Policy: "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net"
enabled: true
environments: ["dev", "prod"] # Only apply in specific environments
The bundle leverages Symfony’s EventSubscriber to inject headers via the kernel.response event. Extend this pattern for custom logic:
// src/EventSubscriber/CustomHeaderSubscriber.php
namespace App\EventSubscriber;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class CustomHeaderSubscriber extends \Duc01nguyen\SecurityHeadersBundle\EventSubscriber\SecurityHeadersSubscriber
{
public function onKernelResponse(ResponseEvent $event)
{
$response = $event->getResponse();
$response->headers->set('X-Custom-Header', 'value');
}
public static function getSubscribedEvents()
{
return [KernelEvents::RESPONSE => ['onKernelResponse', 15]];
}
}
Use twig functions or services to generate dynamic headers:
# config/packages/duc01nguyen_security_headers.yaml
duc01nguyen_security_headers:
headers:
X-Request-ID: "%kernel.environment%" # Placeholder
// src/Twig/Extension/SecurityHeaderExtension.php
namespace App\Twig\Extension;
class SecurityHeaderExtension extends \Twig\Extension\AbstractExtension
{
public function getFunctions()
{
return [
new \Twig\TwigFunction('generate_csp', [$this, 'generateCsp']),
];
}
public function generateCsp()
{
return "default-src 'self'; img-src 'self' data:";
}
}
Split configurations by environment:
# config/packages/dev/duc01nguyen_security_headers.yaml
duc01nguyen_security_headers:
headers:
X-Powered-By: "Symfony (Dev)"
enabled: true
# config/packages/prod/duc01nguyen_security_headers.yaml
duc01nguyen_security_headers:
headers:
X-Powered-By: "Symfony"
Strict-Transport-Security: "max-age=31536000; includeSubDomains"
enabled: true
Header Overrides
Headers set in SecurityHeadersBundle can be overridden by:
HttpCache).
Fix: Use priority: 255 in getSubscribedEvents() to ensure last execution.CSP Misconfigurations
Incorrect Content-Security-Policy can break your app. Test with:
curl -H "Content-Security-Policy: default-src 'none'" http://your-app.dev
Tip: Use Report-Only mode first:
duc01nguyen_security_headers:
headers:
Content-Security-Policy-Report-Only: "default-src 'self'"
Environment Mismatches
Headers may not apply if enabled: false or environments excludes the current env.
Debug: Check kernel.debug and kernel.environment in ResponseEvent.
Log Headers Add a subscriber to log headers:
public function onKernelResponse(ResponseEvent $event)
{
$headers = $event->getResponse()->headers->all();
\Symfony\Component\Debug\Debug::dump($headers);
}
Disable Headers Temporarily
Set enabled: false in config or comment out the bundle in bundles.php.
Add Custom Headers via Service
Bind a service to duc01nguyen_security_headers.header_provider:
services:
App\Service\CustomHeaderProvider:
tags:
- { name: duc01nguyen_security_headers.header_provider }
Override Default Headers
Extend the bundle’s SecurityHeadersSubscriber and override getHeaders():
class CustomSecurityHeadersSubscriber extends \Duc01nguyen\SecurityHeadersBundle\EventSubscriber\SecurityHeadersSubscriber
{
public function getHeaders(): array
{
return [
'X-Custom-Header' => 'overridden',
// ... other headers
];
}
}
Conditional Header Injection
Use Symfony’s ParameterBag to conditionally add headers:
if ($this->container->getParameter('feature.flags.csp_enabled')) {
$headers['Content-Security-Policy'] = "default-src 'self'";
}
How can I help you explore Laravel packages today?