Skip to content
Manages application of security headers with many safe defaults
Branch: master
Clone or download

Latest commit

jobertabma and oreoshake Raise on override defined config (#436)
* Raise exception when trying to override an existing config

* Raise exception when trying to set a named append more than once

* Update documentation

* Raise an exception when a named append or override with the same name exists

Co-authored-by: Neil Matatall <>
Latest commit 38a59ce Mar 26, 2020


Type Name Latest commit message Commit time
Failed to load latest commit information.
.github Run actions on pull_request, not push Mar 26, 2020
docs Raise on override defined config (#436) Mar 26, 2020
lib Raise on override defined config (#436) Mar 26, 2020
spec Raise on override defined config (#436) Mar 26, 2020
.gitignore cleanup .gitignore Mar 1, 2016
.rspec remove rspec block notation from Rakefile Jun 2, 2017
.rubocop.yml Add rubocop-performance gem and config to fix deprecation message (#430) Feb 20, 2020
.ruby-gemset rvmrc change Jun 9, 2014
.ruby-version ok, maybe not that recent Jan 8, 2020 bump to 6.3 Jan 21, 2020 Create Apr 21, 2017 Update Jun 20, 2017
Gemfile Add rubocop-performance gem and config to fix deprecation message (#430) Feb 20, 2020
Guardfile fix rubocop violations Jun 2, 2017
LICENSE Do years even matter? Jan 21, 2020 replace travis badge with actions badge Feb 21, 2020
Rakefile remove rspec block notation from Rakefile Jun 2, 2017
secure_headers.gemspec clean up some linter errors showing up in newer CI Jun 26, 2019

Secure Headers Build + Test Code Climate Coverage Status

master represents 6.x line. See the upgrading to 4.x doc, upgrading to 5.x doc, or upgrading to 6.x doc for instructions on how to upgrade. Bug fixes should go in the 5.x branch for now.

The gem will automatically apply several headers that are related to security. This includes:

It can also mark all http cookies with the Secure, HttpOnly and SameSite attributes. This is on default but can be turned off by using config.cookies = SecureHeaders::OPT_OUT.

secure_headers is a library with a global config, per request overrides, and rack middleware that enables you customize your application settings.



If you do not supply a default configuration, exceptions will be raised. If you would like to use a default configuration (which is fairly locked down), just call SecureHeaders::Configuration.default without any arguments or block.

All nil values will fallback to their default values. SecureHeaders::OPT_OUT will disable the header entirely.

Word of caution: The following is not a default configuration per se. It serves as a sample implementation of the configuration. You should read more about these headers and determine what is appropriate for your requirements.

SecureHeaders::Configuration.default do |config|
  config.cookies = {
    secure: true, # mark all cookies as "Secure"
    httponly: true, # mark all cookies as "HttpOnly"
    samesite: {
      lax: true # mark all cookies as SameSite=lax
  # Add "; preload" and submit the site to for best protection.
  config.hsts = "max-age=#{1.week.to_i}"
  config.x_frame_options = "DENY"
  config.x_content_type_options = "nosniff"
  config.x_xss_protection = "1; mode=block"
  config.x_download_options = "noopen"
  config.x_permitted_cross_domain_policies = "none"
  config.referrer_policy = %w(origin-when-cross-origin strict-origin-when-cross-origin)
  config.csp = {
    # "meta" values. these will shape the header, but the values are not included in the header.
    preserve_schemes: true, # default: false. Schemes are removed from host sources to save bytes and discourage mixed content.
    disable_nonce_backwards_compatibility: true, # default: false. If false, `unsafe-inline` will be added automatically when using nonces. If true, it won't. See #403 for why you'd want this.

    # directive values: these values will directly translate into source directives
    default_src: %w('none'),
    base_uri: %w('self'),
    block_all_mixed_content: true, # see
    child_src: %w('self'), # if child-src isn't supported, the value for frame-src will be set.
    connect_src: %w(wss:),
    font_src: %w('self' data:),
    form_action: %w('self',
    frame_ancestors: %w('none'),
    img_src: %w( data:),
    manifest_src: %w('self'),
    media_src: %w(,
    object_src: %w('self'),
    sandbox: true, # true and [] will set a maximally restrictive setting
    plugin_types: %w(application/x-shockwave-flash),
    script_src: %w('self'),
    style_src: %w('unsafe-inline'),
    worker_src: %w('self'),
    upgrade_insecure_requests: true, # see
    report_uri: %w(
  # This is available only from 3.5.0; use the `report_only: true` setting for 3.4.1 and below.
  config.csp_report_only = config.csp.merge({
    img_src: %w(,
    report_uri: %w(

Default values

All headers except for PublicKeyPins and ClearSiteData have a default value. The default set of headers is:

Content-Security-Policy: default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
Strict-Transport-Security: max-age=631138519
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Frame-Options: sameorigin
X-Permitted-Cross-Domain-Policies: none
X-Xss-Protection: 1; mode=block

API configurations

Which headers you decide to use for API responses is entirely a personal choice. Things like X-Frame-Options seem to have no place in an API response and would be wasting bytes. While this is true, browsers can do funky things with non-html responses. At the minimum, we suggest CSP:

SecureHeaders::Configuration.override(:api) do |config|
  config.csp = { default_src: 'none' }
  config.hsts = SecureHeaders::OPT_OUT
  config.x_frame_options = SecureHeaders::OPT_OUT
  config.x_content_type_options = SecureHeaders::OPT_OUT
  config.x_xss_protection = SecureHeaders::OPT_OUT
  config.x_permitted_cross_domain_policies = SecureHeaders::OPT_OUT

However, I would consider these headers anyways depending on your load and bandwidth requirements.


This project originated within the Security team at Twitter. An archived fork from the point of transition is here:

Contributors include:

  • Neil Matatall @oreoshake
  • Chris Aniszczyk
  • Artur Dryomov
  • Bjørn Mæland
  • Arthur Chiu
  • Jonathan Viney
  • Jeffrey Horn
  • David Collazo
  • Brendon Murphy
  • William Makley
  • Reed Loden
  • Noah Kantrowitz
  • Wyatt Anderson
  • Salimane Adjao Moustapha
  • Francois Chagnon
  • Jeff Hodges
  • Ian Melven
  • Darío Javier Cravero
  • Logan Hasson
  • Raul E Rangel
  • Steve Agalloco
  • Nate Collings
  • Josh Kalderimis
  • Alex Kwiatkowski
  • Julich Mera
  • Jesse Storimer
  • Tom Daniels
  • Kolja Dummann
  • Jean-Philippe Doyle
  • Blake Hitchcock
  • vanderhoorn
  • orthographic-pedant
  • Narsimham Chelluri

If you've made a contribution and see your name missing from the list, make a PR and add it!

Similar libraries

You can’t perform that action at this time.