idez/nova-security
A security toolkit for Laravel Nova that adds two-factor authentication, password policies, login throttling, and audit logging to protect your admin panel and keep sensitive resources safe.
Before writing a single line of integration code, treat this as an evaluation, not an installation. idez/nova-security has no stars and no dependents, and its description ("a collection of different techniques and measures to make your laravel app more secure") is broad rather than specific. The name suggests Laravel Nova tooling, but the description suggests a grab-bag of general hardening measures (headers, middleware, config flags). Those two things imply very different integration work, so your first job is to resolve that ambiguity.
Minimal steps:
composer.json — what Laravel/PHP versions it supports, what it requires, and whether it declares a extra.laravel.providers entry (auto-discovery).src/ — is the bulk middleware, a service provider, config stubs, or Nova Tool/Card classes?config/ — a published config file is your primary control surface; read it first, because every day-to-day decision (which measures are on, which routes they apply to) will be a key in there.composer require idez/nova-security and immediately confirm the resolved version in composer.lock. Given the adoption profile, do not accept a ^ constraint you haven't reviewed.php artisan vendor:publish --provider=..., or --tag= if one is defined) and commit it, so behavior is reviewable in PRs rather than hidden in vendor.boot() (what it registers, what it aliases) → the middleware classes (what they actually mutate on the request/response) → the README, which for a low-adoption package is often aspirational rather than accurate.local/staging with only the response-header middleware enabled — then diff actual HTTP responses with curl -I before and after. That gives you a ground-truth picture of what it does in ~10 minutes, which is faster than reading and guessing.If the package turns out to be Nova-specific, your "first use case" is registering the tool in a Nova service provider and checking it appears in the sidebar for an authorized user. If it is general middleware, your first use case is security headers on the web group. Confirm which before planning further work.
Treat the package as a source of copyable measures, not a dependency. For a package with 0 stars, 0 dependents, and an unscoped description, the highest-value pattern is often to vendor the specific middleware you want into app/Http/Middleware/ with attribution, rather than take a runtime dependency. You get: full visibility in code review, no supply-chain surface, no version-compatibility debt when you upgrade Laravel, and the ability to fix a bug yourself. Reserve the real dependency for the case where it is genuinely Nova-coupled (tool classes, Vue assets) and vendoring would be painful.
Wrap measures behind environment-scoped config. Whatever the config keys are, don't sprinkle booleans across middleware and providers. Centralize:
// config/security.php — a thin layer over the package's own config
return [
'headers' => env('SECURITY_HEADERS', true),
'hsts' => env('SECURITY_HSTS', app()->isProduction()),
'csp' => env('SECURITY_CSP', false), // opt-in, see gotchas
];
Then gate the package's middleware or bindings on that config in AppServiceProvider::boot(). This lets you ship the package to production with a subset enabled while keeping staging aggressive.
Middleware stacking, not middleware replacement. Register the package's middleware alongside your existing ones rather than swapping anything out. A typical stack for the web group: TrustProxies → your session/auth middleware → package security-headers middleware → AddQueuedCookiesToResponse → ... Order matters most for headers, because the last middleware to touch the response wins. Add it late in the stack if you want it to override, early if you want to be overridden.
Apply narrowly first, broaden later. Use route groups to scope it:
Route::middleware(['web', 'nova-security'])->group(function () {
// authenticated app area
});
Then widen to the web group once you've verified nothing breaks. Never put it on the api group without checking CORS/preflight behavior — stricter headers routinely break browser calls from a separate origin.
Pair with tests that assert the behavior, not the package. Write feature tests that hit a route and assert on headers/status, so that if you later rip the package out and reimplement it yourself, the tests still pass:
public function test_security_headers_are_present(): void
{
$this->get('/')->assertHeader('X-Frame-Options', 'DENY');
}
This is the single most valuable pattern here — it decouples your guarantees from a dependency you may not keep.
Integration tip for Nova users: if the package ships Nova tools/cards, register them in app/Providers/NovaServiceProvider.php → tools() / cards(), and gate with Gate::define or the tool's canSee/authorize callback. Security tooling that is visible to non-admins is itself a vulnerability.
composer.json supports your Laravel major, and whether there are open issues. If it's abandoned, vendor the code.onclick=, Livewire/Alpine, and third-party analytics to fail with console-only errors. Start with Content-Security-Policy-Report-Only, ship to staging, and collect violations before enforcing. Never enable CSP in a single PR alongside anything else.Strict-Transport-Security, ensure it is gated on production + HTTPS + a verified certificate chain. A stray HSTS header from a staging host can lock you out of a subdomain for max-age seconds, and browsers ignore the includeSubDomains rollback attempts.curl -I https://prod-host), not just from php artisan serve. Duplicate or contradictory headers are worse than none.X-Frame-Options vs frame-ancestors. If both are emitted and disagree, behavior is browser-dependent. Pick one strategy and remove the other.extra.laravel.providers is set, the provider boots on install before you've published config. Publish config in the same PR as the composer require to keep the diff atomic and revertable.logger()->info('nova-security', [...])) or use php artisan route:list -v to confirm the middleware is actually attached to the routes you think it is. The most common failure mode with security middleware is that it silently isn't in the stack.$this->app->bind(...)), publishes views/translations, or exposes an event. If it offers none of these, it will be hard to customize without forking — another reason to vendor.How can I help you explore Laravel packages today?