Latest Posts

Categories

Categories

nginx header inconsistency, aka setting headers all the way down

For the three visitors I attract in a month, I’ve had an outsized interest in making this the most secure WordPress site that I can. My focus of late has been primarily on the security-related headers I can set. In particular, ensuring that HSTS and HPKP were present on all requests became a priority.

Why?

A few weeks ago, I noticed that certain assets served from my CDN host lacked the Strict Transport Security headers (HSTS) I’d expected. To the best of my knowledge, I’d configured nginx to set these headers on every request.

Further testing showed that, seemingly randomly, multiple of the headers I’d set would disappear. A lot of searching led me to an insightful response on serverfault:

This is expected behaviour. The add_header directives, much like all other array-type directives in nginx, are inherited from the previous level if and only if there are no add_header directives defined on the current level.

What this meant for me is that as I’d introduced add_header calls in nginx location blocks, specifically as part of enabling microcaching, I’d inadvertently blocked my previously-set HSTS headers, among others.

A Solution?

Unfortunately or not, nginx interprets all include directives when the daemon starts, which means run-time includes aren’t possible. Practically for me, this posed a problem ensuring that domain-specific headers were set, namely those pertaining to “header public key pinning” (HPKP).

My solution, short of using a tool such as Puppet or Chef to build singular, comprehensive configurations, was to break the nginx config into many, smaller includes. Generally:

The remaining included files contain nothing particularly interesting that isn’t common to serving WordPress (or most PHP applications) via nginx.

My Point

The important detail is that every location block is re-defined within every server block, using shared files via include. This allows me to drop arbitrary changes into each location block, while also maintaining any headers set previously. It also, sadly, requires duplicating location blocks amongst server blocks that only differ by the domains supported in their SSL certificates.

In my particular case, I can include the relevant HPKP declaration for the domain given in server_name, avoiding any crazy SSL errors. Including the various headers in every block also ensures that HSTS and certain security headers are always present. The amount of duplication is acceptable, in my opinion, given the secure headers this configuration consistently sets.

I’m especially pleased that these headers carry through on assets served via my CDN, KeyCDN.

In Short…

nginx doesn’t inherit headers at the current level if add_headers is called at the same level, so plan accordingly and consider include directives.