ctrlwebinc / security-headers-checker
A Laravel package to check HTTP security headers
Package info
github.com/ctrlwebinc/security-headers-checker
pkg:composer/ctrlwebinc/security-headers-checker
Requires
- php: ^8.5
- illuminate/console: ^12.0
- illuminate/http: ^12.0
- illuminate/routing: ^12.0
- illuminate/support: ^12.0
Requires (Dev)
- orchestra/testbench: ^10.0
- phpunit/phpunit: ^12.0
This package is not auto-updated.
Last update: 2026-04-24 13:35:46 UTC
README
A Laravel 12 package to check HTTP security headers.
Detects missing, misconfigured, and deprecated security headers, exposes an Artisan command and a REST API endpoint, and returns a security score.
Requirements
- PHP 8.5+
- Laravel 12
Installation
composer require ctrlwebinc/security-headers-checker
The package auto-discovers itself via Laravel's package auto-discovery. No manual registration needed.
Publish the config (optional)
php artisan vendor:publish --tag=security-headers-checker-config
This creates config/security-headers-checker.php where you can customize headers, timeouts, SSL verification, route prefix, and middleware.
Usage
Artisan command
# Basic check php artisan security-headers:check https://example.com # With all options php artisan security-headers:check https://example.com \ --information \ --caching \ --deprecated \ --json # Custom port php artisan security-headers:check https://example.com --port=8443 # With cookie and proxy php artisan security-headers:check https://example.com \ --cookie="session=abc123" \ --proxy=http://127.0.0.1:8080 # Custom request header (repeatable) php artisan security-headers:check https://example.com \ --add-header="Authorization: Bearer mytoken" \ --add-header="X-Custom: value" # Disable SSL verification php artisan security-headers:check https://self-signed.example.com --disable-ssl # Use GET instead of HEAD php artisan security-headers:check https://example.com --use-get
Available options
| Option | Short | Description |
|---|---|---|
--port= |
-p |
Custom port |
--cookie= |
-c |
Cookie string |
--add-header= |
-a |
Extra header (repeatable) |
--disable-ssl |
-d |
Skip SSL/TLS verification |
--use-get |
-g |
GET instead of HEAD |
--json |
-j |
JSON output |
--information |
-i |
Show informational headers |
--caching |
-x |
Show cache headers |
--deprecated |
-k |
Show deprecated headers |
--proxy= |
Proxy URL |
REST API
The package registers a single endpoint:
POST /api/security-headers-checker/analyze
Content-Type: application/json
Request body
{
"url": "https://example.com",
"use_get": false,
"disable_ssl": false,
"cookies": "session=abc123",
"headers": { "Authorization": "Bearer token" },
"proxy": "http://127.0.0.1:8080",
"show_information": false,
"show_caching": false,
"show_deprecated": false
}
Response
{
"success": true,
"data": {
"url": "https://example.com",
"status_code": 200,
"score": 75,
"present": [
{ "header": "Strict-Transport-Security", "value": "max-age=31536000", "flag": null }
],
"missing": [
{ "header": "Content-Security-Policy", "flag": "warning" }
],
"insecure": [
{
"header": "X-Frame-Options",
"value": "ALLOWALL",
"reason": "Value 'ALLOWALL' is not DENY or SAMEORIGIN."
}
],
"deprecated": [],
"information": [],
"caching": []
}
}
To disable the built-in routes and define your own:
// config/security-headers-checker.php 'routes' => [ 'enabled' => false, ],
Then in routes/api.php:
use Ctrlwebinc\SecurityHeadersChecker\Http\Controllers\SecurityHeadersController; Route::post('/my-path/analyze', [SecurityHeadersController::class, 'analyze']);
Facade
use Ctrlwebinc\SecurityHeadersChecker\Facades\SecurityHeadersChecker; $result = SecurityHeadersChecker::analyze('https://example.com', options: [ 'disable_ssl' => true, ], showInformation: true); $score = SecurityHeadersChecker::score($result);
Dependency injection
use Ctrlwebinc\SecurityHeadersChecker\Services\SecurityHeadersService; class MyController extends Controller { public function __construct(private SecurityHeadersService $checker) {} public function check(Request $request): JsonResponse { $result = $this->checker->analyze($request->input('url')); return response()->json(['score' => $this->checker->score($result)]); } }
Configuration
After publishing, edit config/security-headers-checker.php:
return [ 'routes' => [ 'enabled' => true, 'prefix' => 'api/security-headers-checker', 'middleware' => ['api'], ], 'http' => [ 'timeout' => 15, 'verify_ssl' => true, 'use_get' => false, ], // Customize which headers are checked and their severity: // null = critical, 'warning' = best practice, 'deprecated' = should be removed 'security_headers' => [ 'Strict-Transport-Security' => null, 'Content-Security-Policy' => 'warning', 'Expect-CT' => 'deprecated', // ... ], ];
Environment variables:
SECURITY_HEADERS_CHECKER_ROUTES_ENABLED=true SECURITY_HEADERS_CHECKER_ROUTE_PREFIX=api/security-headers-checker SECURITY_HEADERS_CHECKER_TIMEOUT=15 SECURITY_HEADERS_CHECKER_VERIFY_SSL=true
Security score
The score (0–100) is calculated as:
score = (present_critical_headers / total_critical_headers) × 100 − (insecure_count × 10)
| Score | Rating |
|---|---|
| 70–100 | ✅ Good |
| 40–69 | ⚠️ Needs improvement |
| 0–39 | ❌ Insufficient |
Headers checked
Critical (absence = failure)
Strict-Transport-SecurityX-Frame-OptionsX-Content-Type-OptionsX-XSS-Protection
Warning (absence = best-practice warning)
Content-Security-PolicyReferrer-PolicyPermissions-PolicyCross-Origin-Embedder-PolicyCross-Origin-Resource-PolicyCross-Origin-Opener-Policy
Deprecated (presence = warning)
X-Permitted-Cross-Domain-PoliciesExpect-CT
Informational (shown with --information)
Server,X-Powered-By,X-AspNet-Version,X-AspNetMvc-Version
Cache (shown with --caching)
Cache-Control,Pragma,Last-Modified,Expires,ETag
Testing
composer install ./vendor/bin/phpunit
License
MIT
Author
Daniel Le Blanc — daniel.leblanc@ctrlweb.ca — ctrlweb.ca