Mozilla's Observatory

— Bryan Allred

Mozilla has recently publicly opened up a new tool Observatory at The tool aims to help developers and security professionals be aware of potential issues and help resolve them for a more safe and secure Internet. This will be a dive in to targetting our own website at and then listing the issues and how we resolved or justified them. The actual results can be viewed here.

First, we should probably declare where we will be making the modifications. Our server is running on Caddy which hosts content directly but also acts as a proxy to other internally hosted services (i.e. Hugo, Gogs, etc.). For this example we wanted to focus on our main site which is ran using Hugo on an internal port which Caddy then proxies all requests to. The nice thing about this setup is:

  1. Hugo can be automatically updated via source control
  2. Caddy can host multiple services and subdomains
  3. Caddy can handle the SSL and TLS configurations automatically leveraging Let’s Encrypt and the ACME protocol
  4. We can explicitly allow paths to bypass the proxy to serve specific static resources

Below is a clean version of the configuration: {
    proxy / localhost:5097 {
        except /.well-known
    root /var/www/

To summarize we went from an F to an A+ with an hour investment of time.

Content Security Policy

    header / {
        Content-Security-Policy "default-src 'none';
                                 img-src 'self' *;
                                 object-src 'none';
                                 script-src 'self';
                                 style-src 'self' 'unsafe-inline';
                                 connect-src 'self' *;
                                 frame-src 'self' *"

Directive Value Description
default-src ‘none’ If we missed a directive or a new one is added we want to fail hard first.
Our fonts come from Google (which has two addresses) and a CDN.
img-src ‘self’
Images should come from our local site or
object-src ‘none’ We hate embedding objects :)
script-src ‘self’
All our scripts should be local, from a CDN, or from for payments.
style-src ‘self’
Most of our styles will come from local, CDN or Google. However, some inline styles are applied and for a quick turn around we went ahead and approved this. In the future we will be moving all inline styles in to our stylesheet since it is the proper thing to do.
connect-src ‘self’
All XMLHttpRequest, WebSocket, or EventSource requests should be from ourselves or
frame-src ‘self’
Inititially we thought we didn’t load any frames, but it appears’s Checkout API does.

This does still present errors within the console similar to

Refused to execute inline script because it violates the following Content Security Policy directive: “script-src ‘self’”. Either the ‘unsafe-inline’ keyword, a hash (‘sha256-yUVnIIasrC7yNIGYh0wvG7kUNfyCWyaJTAY45Bdqztk=’), or a nonce (‘nonce-…’) is required to enable inline execution.

This was due to the Google Analytics script being in the header of our HTML. Moving this in to its own file resolved the issue.

HTTP Strict Transport Security

    header / {
        # Only connect to this site and subdomains via HTTPS for the next year and also include in the preload list
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

Due to there being subdomains we wanted them included as well, but pretty much this is verbatim to the recommendation.

Subresource Integrity

    <script type="text/javascript" src="" integrity="sha384-CgeP3wqr9h5YanePjYLENwCTSSEz42NJkbFpAFgHWQz7u3Zk8D00752ScNpXqGjS" crossorigin="anonymous"></script>
    <script type="text/javascript" src="" integrity="sha384-jTuHJ2QIy2SvtA4DSlQe6o/OA1yrU7l8JHdmP1PSSeVohsNTdO7fYmcZVie/Ev/l" crossorigin="anonymous"></script>

So this was a fun one and one that was actually on our TODO list. For external script references it is possible to include an additional sanity check on the contents to ensure it has not been tampered with. This is accomplished by the browser by comparing the checksum of the external resource to one predefined within the source. We used to generate our hashes for the added benefit of also determining if the external resource is CORS compliant as well.


    header / {
        # Prevent browsers from incorrectly detecting non-scripts as scripts
        X-Content-Type-Options "nosniff"

Nothing crazy here and just implemented the suggested header.


    header / {
        Content-Security-Policy "...; frame-ancestors 'none'"

        # Block site from being framed
        X-Frame-Options "DENY"

We went ahead and covered both areas (Content-Site-Policy and X-Frame-Options) just to be safe. Right now there are no uses of IFRAMEs within the site, but if there were we could make the appropriate adjustments which are described in more detail in the associated link.

X-XSS-Protection header not implemented

    header / {
        # Block pages from loading when they detect reflected XSS attacks
        X-XSS-Protection "1; mode=block"

Again just implemented the suggested fix and tested to make sure nothing was broken.