ctrlwebinc/security-headers-checker

A Laravel package to check HTTP security headers

Maintainers

Package info

github.com/ctrlwebinc/security-headers-checker

pkg:composer/ctrlwebinc/security-headers-checker

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.0.3 2026-04-23 15:15 UTC

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-Security
  • X-Frame-Options
  • X-Content-Type-Options
  • X-XSS-Protection

Warning (absence = best-practice warning)

  • Content-Security-Policy
  • Referrer-Policy
  • Permissions-Policy
  • Cross-Origin-Embedder-Policy
  • Cross-Origin-Resource-Policy
  • Cross-Origin-Opener-Policy

Deprecated (presence = warning)

  • X-Permitted-Cross-Domain-Policies
  • Expect-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.cactrlweb.ca