oihana / php-middleware
Composable PHP middleware helpers โ security headers (HSTS, CSP, X-Frame-Options, Referrer-Policy, X-Content-Type-Options), CORS with preflight, CSRF, request-id, maintenance mode, rate limiting. PSR-7 compatible, zero magic strings.
Requires
- php: >=8.4
- oihana/php-enums: dev-main
- oihana/php-http: dev-main
- psr/http-message: ^2.0
Requires (Dev)
- nunomaduro/collision: ^8.8
- phpdocumentor/shim: ^3.8
- phpunit/phpunit: ^12
- slim/psr7: ^1.7
Suggests
- oihana/php-memcached: Provides MemcachedRateLimitStore (production-grade backend for enforceRateLimit()).
README
Composable PHP HTTP middleware helpers.
Part of the Oihana PHP ecosystem, this package ships procedural helpers for HTTP security headers, CORS, CSRF, request-id propagation, maintenance mode, fixed-window rate limiting, observability and content negotiation โ PSR-7 compatible, zero magic strings.
๐ Documentation
Full API reference (generated with phpDocumentor): https://bcommebois.github.io/oihana-php-middleware
User guides (FR + EN) live under wiki/.
๐ฆ Installation
Requires PHP 8.4+. Install via Composer:
composer require oihana/php-middleware
โจ What you can do
Security headers
withSecurityHeaders()โ single helper to apply HSTS,Content-Security-Policy,X-Frame-Options,Referrer-Policy,X-Content-Type-Options, the three Cross-Origin policies (COOP / COEP / CORP) andPermissions-Policyto a PSR-7Responsein one call. Typed values viaReferrerPolicy,FrameOptions,CrossOriginOpenerPolicy,CrossOriginEmbedderPolicy,CrossOriginResourcePolicyandPermissionsPolicyFeatureenums โ no magic strings.buildCspHeader()โ compose aContent-Security-Policyvalue from an associative array of directives.CspDirectiveenum exposes the canonical directive names.buildPermissionsPolicyHeader()โ compose aPermissions-Policyvalue with a smart allowlist API (false,true/'*','self', single origin or array โselfstays a token, origins auto-quoted).withDefaultSecurityBaseline()โ opinionated alias ofwithSecurityHeaders()shipping a "safe-for-most-apps" baseline (HSTS 1 year,X-Frame-Options: DENY, nosniff,Referrer-Policy: strict-origin-when-cross-origin, COOP / CORPsame-origin) with caller-supplied overrides merged on top.
CORS
applyCorsHeaders()โ origin allowlist with configurable methods, headers, exposed headers, credentials and max-age. Handles the preflightOPTIONSrequest automatically. Defensive defaults: no*whencredentials = true,Vary: Originadded when the allowlist is dynamic.isCorsRequest()/isCorsPreflight()โ pure predicates so middlewares can short-circuit on same-origin requests or detect a preflight without spelling out the underlying header names.
CSRF
generateCsrfToken()/verifyCsrfToken()โ stateless signed double-submit pattern. Wire format<id>.<exp>.<sig>with a 128-bit base64url random<id>, an absolute expiry timestamp and a base64url HMAC-SHA256 signature keyed by your secret. Optional TTL. Constant-time verification โ never throws on bad input, returnsbool.CsrfFieldenum ships the conventionalcookieandheadernames.
Request ID
requestIdFromRequest()โ readsX-Request-Idfrom the incoming request and returns it when it passes a conservative shape check (1 to 128 chars, URL-safe alphabet), otherwise generates a fresh 128-bit base64url identifier. Defense-in-depth against log-pollution attacks via a forged incoming header.withRequestIdHeader()โ stamps the request ID on the response (PSR-7 immutable).RequestIdFieldenum โHEADER_NAME(X-Request-Id) andATTRIBUTE_NAME(requestId) for wiring the propagation through the middleware chain.
Maintenance mode
respondMaintenanceMode()โ turns a PSR-7 response into a clean503 Service Unavailablewith optionalRetry-Afterheader (acceptsintdelta-seconds,DateTimeInterfaceformatted as IMF-fixdate, or rawstring) and optional body.MaintenanceOptionenum carries the option keys.
Rate limiting
enforceRateLimit()โ fixed-window rate-limit enforcement on PSR-7 requests. Identity resolved verbatim fromKEY(string), via callablefn(Request): string, or by fallback to the client IP. Returns an immutableRateLimitDecision(allowed,limit,remaining,reset,retryAfter).withRateLimitHeaders()โ stampsLimit / Remaining / Reseton the response from a decision, plusRetry-Afterwhen blocked. Defaults to the legacyX-RateLimit-*family, opt-in to the RFC 9421 draftRateLimit-*family.RateLimitStoreinterface โ single atomic methodincrement(string $key, int $window): int. ShippedInMemoryRateLimitStore(process-local, for tests and CLI tools) ; production-gradeMemcachedRateLimitStoreinoihana/php-memcached.
Observability
withResponseTime()โ stamps the elapsed processing time on the response. Two output formats: de-factoX-Response-Time: 42.50ms(default) or W3CServer-Timing: total;dur=42.50(opt-in viaResponseTimeOption::USE_SERVER_TIMING).
Content negotiation
negotiateMimeType()โ thin PSR-7 wrapper overoihana\http\helpers\negotiation\negotiate()to select the best server-side MIME type from the clientAcceptheader. Honours RFC 7231 quality values, standard wildcards (universal andtype/*) andq=0explicit refusals.
Under the hood
- Pure PSR-7 โ no framework lock-in. Works with Slim, Laravel, Symfony HTTP Foundation (via PSR-7 bridge), Hyperf, RoadRunner, etc.
- Built on
oihana/php-httpprimitives (IP detection, content negotiation, etc.) andoihana/php-enumstyped HTTP constants (HttpHeader,HttpMethod,HttpStatusCode,AuthScheme, โฆ).
โ Running tests
Run all tests:
composer test
๐ ๏ธ Generate the documentation
composer doc
๐งพ License
Licensed under the Mozilla Public License 2.0 (MPLโ2.0).
๐ค About the author
- Author: Marc ALCARAZ (aka eKameleon)
- Email:
marc@ooop.fr - Website:
https://www.ooop.fr
๐ Related packages
| Package | Description |
|---|---|
oihana/php-http |
Composable PHP HTTP primitives (client IP detection, signed URLs, cookies, content negotiation, โฆ) consumed by this library. |
oihana/php-enums |
Typed HTTP constants (HttpHeader, HttpMethod, HttpStatusCode, AuthScheme, โฆ). |
oihana/php-memcached |
Production-grade MemcachedRateLimitStore implementing this package's RateLimitStore interface. |
