Skip to content

Secure Headers Middleware

Secure Headers Middleware simplifies the setup of security headers. Inspired in part by the capabilities of Helmet, it allows you to control the activation and deactivation of specific security headers.

Import

ts
import { Hono } from 'hono'
import { secureHeaders } from 'hono/secure-headers'
import { Hono } from 'hono'
import { secureHeaders } from 'hono/secure-headers'
ts
import { Hono } from 'https://deno.land/x/hono/mod.ts'
import { secureHeaders } from 'https://deno.land/x/hono/middleware.ts'
import { Hono } from 'https://deno.land/x/hono/mod.ts'
import { secureHeaders } from 'https://deno.land/x/hono/middleware.ts'

Usage

You can use the optimal settings by default.

ts
const app = new Hono()
app.use(secureHeaders())
const app = new Hono()
app.use(secureHeaders())

You can suppress unnecessary headers by setting them to false.

ts
const app = new Hono()
app.use(
  '*',
  secureHeaders({
    xFrameOptions: false,
    xXssProtection: false,
  })
)
const app = new Hono()
app.use(
  '*',
  secureHeaders({
    xFrameOptions: false,
    xXssProtection: false,
  })
)

You can override default header values using a string.

ts
const app = new Hono()
app.use(
  '*',
  secureHeaders({
    strictTransportSecurity: 'max-age=63072000; includeSubDomains; preload',
    xFrameOptions: 'DENY',
    xXssProtection: '1',
  })
)
const app = new Hono()
app.use(
  '*',
  secureHeaders({
    strictTransportSecurity: 'max-age=63072000; includeSubDomains; preload',
    xFrameOptions: 'DENY',
    xXssProtection: '1',
  })
)

Supported Options

Each option corresponds to the following Header Key-Value pairs.

OptionHeaderValueDefault
-X-Powered-By(Delete Header)True
contentSecurityPolicyContent-Security-PolicyUsage: Setting Content-Security-PolicyNo Setting
crossOriginEmbedderPolicyCross-Origin-Embedder-Policyrequire-corpFalse
crossOriginResourcePolicyCross-Origin-Resource-Policysame-originTrue
crossOriginOpenerPolicyCross-Origin-Opener-Policysame-originTrue
originAgentClusterOrigin-Agent-Cluster?1True
referrerPolicyReferrer-Policyno-referrerTrue
reportingEndpointsReporting-EndpointsUsage: Setting Content-Security-PolicyNo Setting
reportToReport-ToUsage: Setting Content-Security-PolicyNo Setting
strictTransportSecurityStrict-Transport-Securitymax-age=15552000; includeSubDomainsTrue
xContentTypeOptionsX-Content-Type-OptionsnosniffTrue
xDnsPrefetchControlX-DNS-Prefetch-ControloffTrue
xDownloadOptionsX-Download-OptionsnoopenTrue
xFrameOptionsX-Frame-OptionsSAMEORIGINTrue
xPermittedCrossDomainPoliciesX-Permitted-Cross-Domain-PoliciesnoneTrue
xXssProtectionX-XSS-Protection0True

Middleware Conflict

Please be cautious about the order of specification when dealing with middleware that manipulates the same header.

In this case, Secure-headers operates and the x-powered-by is removed:

ts
const app = new Hono()
app.use(secureHeaders())
app.use(poweredBy())
const app = new Hono()
app.use(secureHeaders())
app.use(poweredBy())

In this case, Powered-By operates and the x-powered-by is added:

ts
const app = new Hono()
app.use(poweredBy())
app.use(secureHeaders())
const app = new Hono()
app.use(poweredBy())
app.use(secureHeaders())

Setting Content-Security-Policy

ts
const app = new Hono()
app.use(
  '/test',
  secureHeaders({
    reportingEndpoints: [
      {
        name: 'endpoint-1',
        url: 'https://example.com/reports',
      },
    ],
    // -- or alternatively
    // reportTo: [
    //   {
    //     group: 'endpoint-1',
    //     max_age: 10886400,
    //     endpoints: [{ url: 'https://example.com/reports' }],
    //   },
    // ],
    contentSecurityPolicy: {
      defaultSrc: ["'self'"],
      baseUri: ["'self'"],
      childSrc: ["'self'"],
      connectSrc: ["'self'"],
      fontSrc: ["'self'", 'https:', 'data:'],
      formAction: ["'self'"],
      frameAncestors: ["'self'"],
      frameSrc: ["'self'"],
      imgSrc: ["'self'", 'data:'],
      manifestSrc: ["'self'"],
      mediaSrc: ["'self'"],
      objectSrc: ["'none'"],
      reportTo: 'endpoint-1',
      sandbox: ['allow-same-origin', 'allow-scripts'],
      scriptSrc: ["'self'"],
      scriptSrcAttr: ["'none'"],
      scriptSrcElem: ["'self'"],
      styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
      styleSrcAttr: ['none'],
      styleSrcElem: ["'self'", 'https:', "'unsafe-inline'"],
      upgradeInsecureRequests: [],
      workerSrc: ["'self'"],
    },
  })
)
const app = new Hono()
app.use(
  '/test',
  secureHeaders({
    reportingEndpoints: [
      {
        name: 'endpoint-1',
        url: 'https://example.com/reports',
      },
    ],
    // -- or alternatively
    // reportTo: [
    //   {
    //     group: 'endpoint-1',
    //     max_age: 10886400,
    //     endpoints: [{ url: 'https://example.com/reports' }],
    //   },
    // ],
    contentSecurityPolicy: {
      defaultSrc: ["'self'"],
      baseUri: ["'self'"],
      childSrc: ["'self'"],
      connectSrc: ["'self'"],
      fontSrc: ["'self'", 'https:', 'data:'],
      formAction: ["'self'"],
      frameAncestors: ["'self'"],
      frameSrc: ["'self'"],
      imgSrc: ["'self'", 'data:'],
      manifestSrc: ["'self'"],
      mediaSrc: ["'self'"],
      objectSrc: ["'none'"],
      reportTo: 'endpoint-1',
      sandbox: ['allow-same-origin', 'allow-scripts'],
      scriptSrc: ["'self'"],
      scriptSrcAttr: ["'none'"],
      scriptSrcElem: ["'self'"],
      styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
      styleSrcAttr: ['none'],
      styleSrcElem: ["'self'", 'https:', "'unsafe-inline'"],
      upgradeInsecureRequests: [],
      workerSrc: ["'self'"],
    },
  })
)

Released under the MIT License.