HTTP Public Key Pinning (HPKP)

Deprecated
This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.
Note: Public Key Pinning mechanism was deprecated in favor of Certificate Transparency and Expect-CT header.
HTTP Public Key Pinning (HPKP) was a security feature that used to tell a web client to associate a specific cryptographic public key with a certain web server to decrease the risk of MITM attacks with forged certificates. It has been removed in modern browsers and is no longer supported.
To ensure the authenticity of a server's public key used in TLS sessions, this public key is wrapped into a X.509 certificate which is usually signed by a certificate authority (CA). Web clients such as browsers trust a lot of these CAs, which can all create certificates for arbitrary domain names. If an attacker is able to compromise a single CA, they can perform MITM attacks on various TLS connections. HPKP can circumvent this threat for the HTTPS protocol by telling the client which public key belongs to a certain web server.
HPKP is a Trust on First Use (TOFU) technique. The first time a web server tells a client via a special HTTP header which public keys belong to it, the client stores this information for a given period of time. When the client visits the server again, it expects at least one certificate in the certificate chain to contain a public key whose fingerprint is already known via HPKP. If the server delivers an unknown public key, the client should present a warning to the user.
Firefox and Chrome disable pin validation for pinned hosts whose validated certificate chain terminates at a user-defined trust anchor (rather than a built-in trust anchor). This means that for users who imported custom root certificates all pinning violations are ignored.

Enabling HPKP

To enable this feature for your site, you need to return the Public-Key-Pins HTTP header when your site is accessed over HTTPS:
Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"]
pin-sha256
The quoted string is the Base64 encoded Subject Public Key Information (SPKI) fingerprint. It is possible to specify multiple pins for different public keys. Some browsers might allow other hashing algorithms than SHA-256 in the future. See below on how to extract this information out of a certificate or key file.
max-age
The time, in seconds, that the browser should remember that this site is only to be accessed using one of the defined keys.
includeSubDomains Optional
If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
report-uri Optional
If this optional parameter is specified, pin validation failures are reported to the given URL.
Note: The current specification requires including a second pin for a backup key which isn't yet used in production. This allows for changing the server's public key without breaking accessibility for clients that have already noted the pins. This is important for example when the former key gets compromised.

Extracting the Base64 encoded public key information

Note: While the example below shows how to set a pin on a server certificate, it is recommended to place the pin on the intermediate certificate of the CA that issued the server certificate, to ease certificates renewals and rotations.
First you need to extract the public key information from your certificate or key file and encode them using Base64.
The following commands will help you extract the Base64 encoded information from a key file, a certificate signing request, or a certificate.
openssl rsa -in my-rsa-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
openssl ec -in my-ecc-key-file.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
openssl req -in my-signing-request.csr -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
openssl x509 -in my-certificate.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
The following command will extract the Base64 encoded information for a website.
openssl s_client -servername www.example.com -connect www.example.com:443 | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

Example HPKP Header

Public-Key-Pins: 
  pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs="; 
  pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE="; 
  max-age=5184000; includeSubDomains; 
  report-uri="https://www.example.org/hpkp-report"
In this example, pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=" pins the server's public key used in production. The second pin declaration pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=" also pins the backup key. max-age=5184000 tells the client to store this information for two months, which is a reasonable time limit according to the IETF RFC. This key pinning is also valid for all subdomains, which is told by the includeSubDomains declaration. Finally, report-uri="https://www.example.net/hpkp-report" explains where to report pin validation failures.

Report-Only header

Instead of using a Public-Key-Pins header you can also use a Public-Key-Pins-Report-Only header. This header only sends reports to the report-uri specified in the header and does still allow browsers to connect to the webserver even if the pinning is violated.

Setting up your webserver to include the HPKP header

The concrete steps necessary to deliver the HPKP header depend on the web server you use.
Note: These examples use a max-age of two months and include all subdomains. It is advised to verify that this setup will work for your server.
HPKP has the potential to lock out users for a long time if used incorrectly! The use of backup certificates and/or pinning the CA certificate is recommended.

Apache

Adding a line similar to the following to your webserver's config will enable HPKP on your Apache. This requires mod_headers enabled.
Header always set Public-Key-Pins "pin-sha256=\"base64+primary==\"; pin-sha256=\"base64+backup==\"; max-age=5184000; includeSubDomains"

Nginx

Adding the following line and inserting the appropriate pin-sha256="..." values will enable HPKP on your nginx. This requires the ngx_http_headers_module.
add_header Public-Key-Pins 'pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubDomains' always;

Lighttpd

The following line with your relevant key information (pin-sha256="..." fields) will enable HPKP on lighttpd.
setenv.add-response-header  = ( "Public-Key-Pins" => "pin-sha256=\"base64+primary==\"; pin-sha256=\"base64+backup==\"; max-age=5184000; includeSubDomains")
Note: This requires the mod_setenv server.module loaded which can be included by the following if not already loaded.
server.modules += ( "mod_setenv" )

IIS

Add the following line to the Web.config file to send the Public-Key-Pins header:
<system.webServer>
  ...

  <httpProtocol>
    <customHeaders>
      <add name="Public-Key-Pins" value="pin-sha256=&quot;base64+primary==&quot;; pin-sha256=&quot;base64+backup==&quot;; max-age=5184000; includeSubDomains" />
    </customHeaders>
  </httpProtocol>

  ...
</system.webServer>

Specifications

Specification Title
RFC 7469, section 2.1: Public-Key-Pins Public Key Pinning Extension for HTTP

Browser compatibility

The compatibility table in this page is generated from structured data. If you'd like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.
Update compatibility data on GitHub
DesktopMobile
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung Internet
Public-Key-Pins
Deprecated
Chrome No support ? — 72Edge No support NoIE No support NoOpera No support ? — 60Safari No support NoWebView Android No support NoChrome Android No support ? — 72Firefox Android Full support 35Opera Android No support ? — 51Safari iOS No support NoSamsung Internet Android No support ? — 11.0
report-uri
Deprecated
Chrome No support 46 — 72Edge No support NoIE No support NoOpera No support 33 — 60Safari No support NoWebView Android No support NoChrome Android No support ? — 72Firefox Android No support NoOpera Android No support 33 — 51Safari iOS No support NoSamsung Internet Android No support ? — 11.0

Thank you!

Report sent

What happens next?

Our team will review your report. Once we verify the information you have supplied we will update this browser compatability table accordingly.

Can I keep track of my report?

You can join the GitHub repository to see updates and commits for this table data:

Tell us what’s wrong with this table

Our goal is to provide accurate, real values for all our compatibility data tables. Notifying MDN of inaccurate data or supplying new data pushes us further towards our goal of providing 100% real values to the developer community.
Thank you for helping.

Legend

Full support  
Full support
No support  
No support
Deprecated. Not for use in new websites.
Deprecated. Not for use in new websites.
See implementation notes.
See implementation notes.
User must explicitly enable this feature.
User must explicitly enable this feature.

See also

Metadata

Related Topics
  1. HTTP
  2. Guides:
  3. ▶︎Resources and URIs
    1. Identifying resources on the Web
    2. Data URIs
    3. Introduction to MIME Types
    4. Complete list of MIME Types
    5. Choosing between www and non-www URLs
  4. ▶︎HTTP guide
    1. Basics of HTTP
    2. Overview of HTTP
    3. Evolution of HTTP
    4. HTTP Messages
    5. A typical HTTP session
    6. Connection management in HTTP/1.x
    7. Protocol upgrade mechanism
  5. ▶︎HTTP security
    1. Content Security Policy (CSP)
    2. HTTP Public Key Pinning (HPKP)
    3. HTTP Strict Transport Security (HSTS)
    4. Cookie security
    5. X-Content-Type-Options
    6. X-Frame-Options
    7. X-XSS-Protection
    8. Mozilla web security guidelines
    9. Mozilla Observatory
  6. HTTP access control (CORS)
  7. HTTP authentication
  8. HTTP caching
  9. HTTP compression
  10. HTTP conditional requests
  11. HTTP content negotiation
  12. HTTP cookies
  13. HTTP range requests
  14. HTTP redirects
  15. HTTP specifications
  16. Feature policy
  17. References:
  18. ▶︎HTTP headers
    1. Accept
    2. Accept-CH
    3. Accept-CH-Lifetime
    4. Accept-Charset
    5. Accept-Encoding
    6. Accept-Language
    7. Accept-Patch
    8. Accept-Ranges
    9. Access-Control-Allow-Credentials
    10. Access-Control-Allow-Headers
    11. Access-Control-Allow-Methods
    12. Access-Control-Allow-Origin
    13. Access-Control-Expose-Headers
    14. Access-Control-Max-Age
    15. Access-Control-Request-Headers
    16. Access-Control-Request-Method
    17. Age
    18. Allow
    19. Alt-Svc
    20. Authorization
    21. Cache-Control
    22. Clear-Site-Data
    23. Connection
    24. Content-Disposition
    25. Content-Encoding
    26. Content-Language
    27. Content-Length
    28. Content-Location
    29. Content-Range
    30. Content-Security-Policy
    31. Content-Security-Policy-Report-Only
    32. Content-Type
    33. Cookie
    34. Cookie2
    35. Cross-Origin-Resource-Policy
    36. DNT
    37. DPR
    38. Date
    39. Device-Memory
    40. Digest
    41. ETag
    42. Early-Data
    43. Expect
    44. Expect-CT
    45. Expires
    46. Feature-Policy
    47. Forwarded
    48. From
    49. Host
    50. If-Match
    51. If-Modified-Since
    52. If-None-Match
    53. If-Range
    54. If-Unmodified-Since
    55. Index
    56. Keep-Alive
    57. Large-Allocation
    58. Last-Modified
    59. Link
    60. Location
    61. NEL
    62. Origin
    63. Pragma
    64. Proxy-Authenticate
    65. Proxy-Authorization
    66. Public-Key-Pins
    67. Public-Key-Pins-Report-Only
    68. Range
    69. Referer
    70. Referrer-Policy
    71. Retry-After
    72. Save-Data
    73. Sec-Fetch-Dest
    74. Sec-Fetch-Mode
    75. Sec-Fetch-Site
    76. Sec-Fetch-User
    77. Sec-WebSocket-Accept
    78. Server
    79. Server-Timing
    80. Set-Cookie
    81. Set-Cookie2
    82. SourceMap
    83. Strict-Transport-Security
    84. TE
    85. Timing-Allow-Origin
    86. Tk
    87. Trailer
    88. Transfer-Encoding
    89. Upgrade-Insecure-Requests
    90. User-Agent
    91. Vary
    92. Via
    93. WWW-Authenticate
    94. Want-Digest
    95. Warning
    96. X-Content-Type-Options
    97. X-DNS-Prefetch-Control
    98. X-Forwarded-For
    99. X-Forwarded-Host
    100. X-Forwarded-Proto
    101. X-Frame-Options
    102. X-XSS-Protection
  19. ▶︎HTTP request methods
    1. CONNECT
    2. DELETE
    3. GET
    4. HEAD
    5. OPTIONS
    6. PATCH
    7. POST
    8. PUT
    9. TRACE
  20. ▶︎HTTP response status codes
    1. 100 Continue
    2. 101 Switching Protocols
    3. 103 Early Hints
    4. 200 OK
    5. 201 Created
    6. 202 Accepted
    7. 203 Non-Authoritative Information
    8. 204 No Content
    9. 205 Reset Content
    10. 206 Partial Content
    11. 300 Multiple Choices
    12. 301 Moved Permanently
    13. 302 Found
    14. 303 See Other
    15. 304 Not Modified
    16. 307 Temporary Redirect
    17. 308 Permanent Redirect
    18. 400 Bad Request
    19. 401 Unauthorized
    20. 402 Payment Required
    21. 403 Forbidden
    22. 404 Not Found
    23. 405 Method Not Allowed
    24. 406 Not Acceptable
    25. 407 Proxy Authentication Required
    26. 408 Request Timeout
    27. 409 Conflict
    28. 410 Gone
    29. 411 Length Required
    30. 412 Precondition Failed
    31. 413 Payload Too Large
    32. 414 URI Too Long
    33. 415 Unsupported Media Type
    34. 416 Range Not Satisfiable
    35. 417 Expectation Failed
    36. 418 I'm a teapot
    37. 422 Unprocessable Entity
    38. 425 Too Early
    39. 426 Upgrade Required
    40. 428 Precondition Required
    41. 429 Too Many Requests
    42. 431 Request Header Fields Too Large
    43. 451 Unavailable For Legal Reasons
    44. 500 Internal Server Error
    45. 501 Not Implemented
    46. 502 Bad Gateway
    47. 503 Service Unavailable
    48. 504 Gateway Timeout
    49. 505 HTTP Version Not Supported
    50. 506 Variant Also Negotiates
    51. 507 Insufficient Storage
    52. 508 Loop Detected
    53. 510 Not Extended
    54. 511 Network Authentication Required
  21. ▶︎CSP directives
    1. CSP: base-uri
    2. CSP: block-all-mixed-content
    3. CSP: child-src
    4. CSP: connect-src
    5. CSP: default-src
    6. CSP: font-src
    7. CSP: form-action
    8. CSP: frame-ancestors
    9. CSP: frame-src
    10. CSP: img-src
    11. CSP: manifest-src
    12. CSP: media-src
    13. CSP: navigate-to
    14. CSP: object-src
    15. CSP: plugin-types
    16. CSP: prefetch-src
    17. CSP: referrer
    18. CSP: report-to
    19. CSP: report-uri
    20. CSP: require-sri-for
    21. CSP: sandbox
    22. CSP: script-src
    23. CSP: script-src-attr
    24. CSP: script-src-elem
    25. CSP: style-src
    26. CSP: style-src-attr
    27. CSP: style-src-elem
    28. CSP: trusted-types
    29. CSP: upgrade-insecure-requests
    30. CSP: worker-src
  22. ▶︎CORS errors
    1. Reason: CORS disabled
    2. Reason: CORS header 'Access-Control-Allow-Origin' does not match 'xyz'
    3. Reason: CORS header 'Access-Control-Allow-Origin' missing
    4. Reason: CORS header ‘Origin’ cannot be added
    5. Reason: CORS preflight channel did not succeed
    6. Reason: CORS request did not succeed
    7. Reason: CORS request external redirect not allowed
    8. Reason: CORS request not HTTP
    9. Reason: Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’
    10. Reason: Did not find method in CORS header ‘Access-Control-Allow-Methods’
    11. Reason: Multiple CORS header 'Access-Control-Allow-Origin' not allowed
    12. Reason: expected ‘true’ in CORS header ‘Access-Control-Allow-Credentials’
    13. Reason: invalid token ‘xyz’ in CORS header ‘Access-Control-Allow-Headers’
    14. Reason: invalid token ‘xyz’ in CORS header ‘Access-Control-Allow-Methods’
    15. Reason: missing token ‘xyz’ in CORS header ‘Access-Control-Allow-Headers’ from CORS preflight channel
  23. ▶︎Feature-Policy directives
    1. Feature-Policy: accelerometer
    2. Feature-Policy: ambient-light-sensor
    3. Feature-Policy: autoplay
    4. Feature-Policy: battery
    5. Feature-Policy: camera
    6. Feature-Policy: display-capture
    7. Feature-Policy: document-domain
    8. Feature-Policy: encrypted-media
    9. Feature-Policy: fullscreen
    10. Feature-Policy: geolocation
    11. Feature-Policy: gyroscope
    12. Feature-Policy: layout-animations
    13. Feature-Policy: legacy-image-formats
    14. Feature-Policy: magnetometer
    15. Feature-Policy: microphone
    16. Feature-Policy: midi
    17. Feature-Policy: oversized-images
    18. Feature-Policy: payment
    19. Feature-Policy: picture-in-picture
    20. Feature-Policy: publickey-credentials
    21. Feature-Policy: sync-xhr
    22. Feature-Policy: unoptimized-images
    23. Feature-Policy: unsized-media
    24. Feature-Policy: usb
    25. Feature-Policy: vibrate
    26. Feature-Policy: vr
    27. Feature-Policy: wake-lock
    28. Feature-Policy: xr
    29. Feature-Policy: xr-spatial-tracking