Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Http Headers Bundle Laravel Package

mmoreram/http-headers-bundle

A Symfony bundle by Marc Morera for managing HTTP response headers through configuration and annotations, letting you define cache, security, and custom headers declaratively instead of setting them manually in every controller.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the bundle with Composer, pinning a version compatible with your Symfony major (the last tag, v1.0.x, was cut against Symfony 3.x, so on Symfony 4/5 expect to vendor-patch the bundle class):

composer require mmoreram/http-headers-bundle

Register it. On Symfony ≤3 in app/AppKernel.php:

$bundles = [
    // ...
    new Mmoreram\HTTPHeadersBundle\HTTPHeadersBundle(),
];

On Symfony 4+ add it to config/bundles.php (the bundle class is a plain Bundle subclass, so autoload works as long as the event-dispatcher API in its listener still matches your Symfony version — check Resources/config/services.yml and the listener class before relying on it).

Then drop a config file (Symfony ≤3: app/config/config.yml; Symfony 4+: config/packages/http_headers.yaml):

mmoreram_http_headers:
    headers:
        X-Frame-Options: "SAMEORIGIN"
        X-Content-Type-Options: "nosniff"
        Referrer-Policy: "strict-origin-when-cross-origin"

Look here first when something is off:

  1. DependencyInjection/Configuration.php — the authoritative config tree; the exact root alias and the headers key shape live here, so verify against your installed version rather than trusting a blog post.
  2. EventListener/ (or Subscriber/) — the kernel.response listener that actually mutates the Response.
  3. Resources/config/services.yml — the listener's priority and whether it's wired as a tagged subscriber.

First day-to-day use case: a global security-header floor. Put the four headers every browser-facing app should have (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and HSTS on TLS-only prod) in the prod config, one curl -I https://your-app/ to confirm, done. That is the entire value proposition — it keeps header policy out of PHP.

Implementation Patterns

Environment-scoped policy. Keep dev permissive and prod strict by defining the bundle config only in the prod config and letting dev inherit nothing:

# config/packages/prod/http_headers.yaml
mmoreram_http_headers:
    headers:
        Strict-Transport-Security: "max-age=31536000; includeSubDomains; preload"
        Content-Security-Policy: "default-src 'self'"

HSTS and CSP in dev will silently break your own debugging (cached HSTS on localhost:8000 bites you for weeks), so never let them leak below prod.

Layer it under controller-level overrides. Treat the bundle as a default and let controllers own exceptions — the pattern that works is: bundle sets the baseline, controller calls $response->headers->set('Content-Security-Policy', $perRouteCsp) for anything that genuinely differs. Understand which one wins: if the listener uses set(), the bundle runs after the controller and clobbers it; if it uses a "only if absent" guard, the controller wins. Check the listener before designing around overrides.

API vs HTML split. The bundle applies headers unconditionally, which is fine for X-Content-Type-Options but wrong for Content-Security-Policy on a JSON API. Two clean approaches:

  • Define only truly universal headers in the bundle config, and add protocol-specific ones from a kernel event listener.
  • Keep everything in the bundle and add a small response listener that strips headers based on Content-Type after the bundle's listener runs (needs a lower priority).

Sane listener ordering. The bundle registers on kernel.response. Anything that reads or rewrites response headers (your own security listener, a cache-control normalizer, a CORS listener) needs to be ordered relative to it. Give the bundle an explicit priority in its service definition if you must, or your listener a priority of -1 to run after.

Compose with framework / reverse proxies. If a CDN or Varnish sits in front, the headers you set here are only half the story — the edge must be configured to forward them (Vary, and don't let the edge strip HSTS). The bundle is a convenience, not a substitute for edge config.

Test it like a contract. A single functional test per environment asserts the headers, which catches config drift during upgrades:

$client = static::createClient();
$client->request('GET', '/');
$response = $client->getResponse();
$this->assertSame('nosniff', $response->headers->get('X-Content-Type-Options'));

Gotchas and Tips

  • Version drift is the real risk. This package's last release is from November 2015 and it has essentially no user base. On modern Symfony the DI container signature and the Symfony\Component\HttpKernel\Event\FilterResponseEvent → ResponseEvent rename in Symfony 4.3 will break the listener. Budget for a small vendor patch or, honestly, a 20-line kernel.response subscriber that does the same thing with no dependency.
  • Header values must be strings (or arrays for multi-value headers). YAML will happily coerce max-age: 0 to an integer and you'll get a confusing set() mismatch; quote everything.
  • Set-Cookie and other multi-value headers need to be passed as arrays — a scalar value will replace, not append, and the bundle may not support arrays at all depending on version. Verify in the listener.
  • Headers set by the controller may be silently overwritten (or silently not applied — both failures are quiet). Debug with curl -sI -o- -D- http://localhost:8000/ and $response->headers->all() dumped from a throwaway controller; don't guess.
  • Cache invalidation: response headers are frequently cached by the reverse proxy, so a config change requires a cache clear and an edge purge. You'll deploy the fix, see it working in dev, and still get old headers in prod.
  • The Symfony profiler shows the final headers, not who set them. For attribution, put a temporary dump() in the listener or use Xdebug — there's no built-in trace.
  • Extension point is the listener, not the config. Any conditional logic (per-route, per-content-type) belongs in your own listener that runs before/after; don't try to encode conditions into YAML.
  • It's abandoned. With 8 stars, 0 dependents, and a decade without maintenance, treat this as a config-shaped convenience, not an audited security component. If your app's header policy matters (HSTS, CSP), the dependency is worth more trouble than the ~20 lines it saves.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route