horse-security-headers

April 12, 2026 · View on GitHub

Response security headers middleware for the Horse web framework.

License: MIT Delphi FPC Horse Boss


What it does

Adds hardening headers to every HTTP response. The middleware calls Next first (letting your route handler run) and then appends the configured headers to the completed response.

HeaderDefault value
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
Referrer-Policystrict-origin-when-cross-origin
Cache-Controlno-store
Strict-Transport-Security(disabled — opt-in)
Serverunknown (replaces the default server banner)

Installation

boss install github.com/freitasjca/horse-security-headers

Or add to your project's boss.json:

{
  "dependencies": {
    "github.com/freitasjca/horse-security-headers": ">=1.0.0"
  }
}

Usage

Default configuration

uses
  Horse,
  Horse.Middleware.SecurityHeaders;

begin
  THorse.Use(THorseSecurityHeaders.New);   // applies to all responses

  THorse.Get('/ping',
    procedure(Req: THorseRequest; Res: THorseResponse)
    begin
      Res.Send('pong');
    end);

  THorse.Listen(9000);
end.

Strict profile (HTTPS endpoints)

THorse.Use(THorseSecurityHeaders.New(
  THorseSecurityHeadersConfig.Strict));   // adds 1-year HSTS + includeSubDomains

Custom configuration

uses
  Horse,
  Horse.Middleware.SecurityHeaders;

var
  LConfig: THorseSecurityHeadersConfig;
begin
  LConfig                    := THorseSecurityHeadersConfig.Default;
  LConfig.XFrameOptions      := fosameorigin;  // allow embedding by same origin
  LConfig.CacheControlNoStore := False;         // let browsers cache responses
  LConfig.HSTSMaxAge         := 86400;          // HSTS: 1 day
  LConfig.HSTSIncludeSubDomains := True;

  THorse.Use(THorseSecurityHeaders.New(LConfig));

  THorse.Listen(9000);
end.

Configuration reference

type
  TFrameOption = (
    fodeny,        // X-Frame-Options: DENY
    fosameorigin   // X-Frame-Options: SAMEORIGIN
  );

  TReferrerPolicy = (
    rpStrictOriginWhenCrossOrigin,  // strict-origin-when-cross-origin
    rpNoReferrer,                   // no-referrer
    rpSameOrigin                    // same-origin
  );

  THorseSecurityHeadersConfig = record
    XContentTypeOptions:   Boolean;
    XFrameOptions:         TFrameOption;
    ReferrerPolicy:        TReferrerPolicy;
    CacheControlNoStore:   Boolean;
    HSTSMaxAge:            Integer;
    HSTSIncludeSubDomains: Boolean;
    SuppressServerHeader:  Boolean;
    class function Default: THorseSecurityHeadersConfig; static;
    class function Strict:  THorseSecurityHeadersConfig; static;
  end;
FieldDefaultStrictDescription
XContentTypeOptionsTrueTrueEmits X-Content-Type-Options: nosniff. Prevents browsers from MIME-sniffing a response away from the declared content type.
XFrameOptionsfodenyfodenyControls X-Frame-Options. fodeny prevents any framing; fosameorigin allows framing by the same origin.
ReferrerPolicyrpStrictOriginWhenCrossOriginsameControls the Referrer-Policy header. See the table below.
CacheControlNoStoreTrueTrueEmits Cache-Control: no-store. Prevents sensitive responses from being stored in caches.
HSTSMaxAge0 (off)31536000max-age in seconds for Strict-Transport-Security. Set to 0 to suppress the header. Only enable on HTTPS endpoints.
HSTSIncludeSubDomainsFalseTrueAppends ; includeSubDomains to the HSTS header.
SuppressServerHeaderTrueTrueReplaces the Server response header with unknown to reduce information disclosure.

ReferrerPolicy values

EnumHeader valueEffect
rpStrictOriginWhenCrossOriginstrict-origin-when-cross-originSends full URL to same-origin requests; only origin to cross-origin HTTPS; nothing to HTTP.
rpNoReferrerno-referrerNever sends a Referer header.
rpSameOriginsame-originSends full URL only to same-origin requests.

Registration order

This middleware must be registered after any middleware that sets response headers you want to preserve, because it runs after Next and adds to (not replaces) the existing headers. It does not need to be first.

THorse.Use(THorseRequestGuard.New);      // first — rejects invalid requests
THorse.Use(THorseSecurityHeaders.New);   // anywhere — wraps every response
THorse.Get('/api/data', ...);

Provider compatibility

ProviderSource of security headers
Horse + IndyThis middleware is the only source. Register it to harden all responses.
Horse + CrossSocketTResponseBridge.Flush in the CrossSocket provider also injects these headers at the transport layer. Using both is safe — the HTTP specification permits duplicate headers for most of these and browsers apply the most restrictive value. Use this middleware on CrossSocket for defence in depth or to centralise header policy in application code.

License

MIT — see LICENSE.