Skip to content
Secure Header Wrapper for Flask Applications
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


Secure Header Wrapper for Flask Applications. This is intended to be a simplified version of the Twitter SecureHeaders Ruby Gem


Install the extension with using pip, or easy_install. Pypi Link

$ pip install flask-secure-headers

Included Headers

Header Purpose Default Policy
Content-Security-Policy (CSP) Restrict rescources to prevent XSS/other attacks default-src 'self'; report-uri /csp_report
Strict-Transport-Security (HSTS) Prevent downgrade attacks (https to http) max-age=31536000; includeSubDomains
X-Permitted-Cross-Domain-Policies Restrict content loaded by flash master-only
X-Frame-Options Prevent content from being framed and clickjacked sameorigin
X-XSS-Protection IE 8+ XSS protection header 1; mode=block
X-Content-Type-Options IE 9+ MIME-type verification nosniff
X-Download-Options IE 10+ Prevent downloads from opening noopen
Public-Key-Pins (HPKP) Associate host with expected CA or public key max-age=5184000; includeSubDomains; report-uri=/hpkp_report [... no default pins]


Each header policy is represented by a dict of paramaters. View default policies.

  • Policies with a key/value pair are represented as {key:value}
    • Ex: {'mode':'block'} becomes 'mode=block'
  • Policies with just a string value are represented as {'value':parameter}
    • Ex: {'value':'noopen'} becomes 'noopen'
  • Policies with additional string values are represented as {value:Bool}
    • Ex: {'max-age':1,'includeSubDomains':True,'preload':False} becomes 'max-age=1 includeSubDomains'
  • CSP is represented as a list inside the dict {cspPolicy:[param,param]}.
    • Ex: {'script-src':['self']} becomes "script-src 'self'"
    • self, none, nonce-* ,sha*, unsafe-inline, etc are automatically encapsulated
  • HPKP pins are represented by a list of dicts under the 'pins' paramter {'pins':[{hashType:hash}]}
    • Ex: {'pins':[{'sha256':'1234'},{'sha256':'ABCD'}]} becomes 'pin-sha256=1234; pin-sha256=ABCD'



To load the headers into your flask app, import the function:

from flask_secure_headers.core import Secure_Headers
sh = Secure_Headers()
Policy Changes

There are two methods to change the default policies that will persist throughout the application: update(), rewrite()

  • Update will add to an existing policy
  • Rewrite will replace a policy

To update/rewrite, pass a dict in of the desired values into the desired method:

""" update """
# Content-Security-Policy: script-src 'self'; report-uri /csp_report; default-src 'self
# X-Permitted-Cross-Domain-Policies: all
# Public-Key-Pins: max-age=5184000; includeSubDomains; report-uri=/hpkp_report; pin-sha256=1234

""" rewrite """
# Content-Security-Policy: default-src 'none'
Policy Removal

A policy can also be removed by passing None as the value:

# there will be no CSP header
Policy parameter removal

For non-CSP headers that contain multiple paramaters (HSTS and X-XSS-Protection), any paramter other than the first can be removed by passing a value of False:

# will produce X-XSS-Protection: 1

# will produce Strict-Transport-Security: max-age=1; includeSubDomains
Read Only

The HPKP and CSP Headers can be set to "-Read-Only" by passing "'read-only':True" into the policy dict. Examples:

  • Header keys can be written using either '_' or '-', but are case sensitive
    • Acceptable: 'X-XSS-Protection','X_XSS_Protection'
    • Unacceptable: 'x-xss-protection'
  • 3 headers are abreviated
    • CSP = Content-Security-Policy
    • HSTS = Strict-Transport-Security
    • HPKP = Public-Key-Pins

Creating the Wrapper

No Policy Updates

Add the @sh.wrapper() decorator after your app.route(...) decorators for each route to create the headers based on the policy you have created using the update/remove methods (or the default policy if those were not used)

def index():
With Policy Updates

The wrapper() method can also be passed a dict in the same format as update/remove to change policies. These policy changes will only effect that specific route.

A couple notes:

  • Changes here will always update the policy instead of rewrite
  • CSP policy and HPKP pin lists will be merged, not overwritten. See comment below for example.
def index():
# CSP will contain "script-src 'self' 'sha1-klsdjfkl232'"
# HPKP will contain "pins-sha256=1234; pins-sha256=ABCD;"

Policies can also be removed from a wrapper:

def index():
# this route will not include Content-Security-Policy or X-XSS-Protection Headers


You can’t perform that action at this time.