What this app is already promising

Free preview 4 min to read

One line in config/environments/production.rb:

config.force_ssl = true

The SSL course said that turns on three things: the redirect, secure cookies, and Strict-Transport-Security. This course is about the third, and about what its default values commit you to, which is more than most people who wrote that line believe.

The default, checked

Rails' ActionDispatch::SSL holds the defaults, and they are:

ActionDispatch::SSL.default_hsts_options
# => { expires: 63072000, subdomains: true, preload: false }

63,072,000 seconds is two years. And subdomains: true.

So this application, with force_ssl = true and no HSTS options set, sends:

Strict-Transport-Security: max-age=63072000; includeSubDomains

Rails builds it in build_hsts_header, appending ; includeSubDomains when subdomains is true and ; preload when preload is. And preload defaults to false, which is the one conservative choice in the set.

What that means

Every browser that has visited this application over HTTPS has recorded a rule. For the next two years, use HTTPS for this host and every subdomain of it, and do not offer the user a way around a certificate error.

Three consequences, all of them already in force:

Every subdomain must serve valid HTTPS. Not the ones that exist now: every one anyone might visit. An internal tool at metrics.nor.app with a self-signed certificate is unreachable for anybody who has visited the main site, with no click-through.

It is not undone by removing the header. Browsers already hold the rule. Deleting the line stops new visitors receiving it and does nothing for the ones who already have it, for up to two years. Lesson 4 is about the procedure that works.

A certificate mistake becomes an outage. Without HSTS, an expired certificate shows a warning a determined person can click past. With it, the browser refuses, and there is no click-through, which is the point, and it removes the escape hatch you might have been relying on.

flowchart TB
  F["config.force_ssl = true"] --> D["Rails default_hsts_options"]
  D --> H["max-age=63072000; includeSubDomains"]
  H --> B["Every visitor's browser records it"]
  B --> S["Every subdomain must be HTTPS, for 2 years"]
  B --> C["A certificate error is now a hard failure"]
  B --> R["Removing the line does not undo it"]

Is that the right setting?

Mostly yes, and it is worth being clear that it was a default rather than a decision.

Two years is the standard strong value. The preload list requires at least one year, and hstspreload.org recommends two. Nobody serious argues for a shorter permanent value; the argument for a shorter one is about the ramp during rollout, which is lesson 4.

includeSubDomains is the right default and the expensive one. It is what makes HSTS meaningful (a session cookie scoped to .nor.app is only as safe as the least secure subdomain) and it is also the clause that turns an unrelated internal tool into an outage. Lesson 3 is the inventory it obliges you to keep.

What would be wrong is not knowing. An application that inherited two years of subdomain-wide HTTPS enforcement from a framework default. And whose team believes they enabled "a redirect", has made a commitment nobody in the room can describe.

Setting it explicitly

config.ssl_options = {
  redirect: { exclude: ->(request) { request.path == "/up" } },
  hsts: { expires: 2.years, subdomains: true, preload: false }
}

The same values, written down. That is the change worth making before anything else in this course. Not because the behavior changes, but because the next person to read the file learns what the application promises without having to know a framework default.

What this course covers

  1. What this app is already promising (this lesson).
  2. How HSTS works.
  3. includeSubDomains and the inventory it obliges.
  4. Ramping max-age, and disabling.
  5. The preload list.
  6. HSTS in context.
  7. Putting it together: an HSTS plan.

Key takeaways

  • force_ssl = true sends max-age=63072000; includeSubDomains (two years, all subdomains) from Rails' defaults.
  • preload defaults to false, which is the one conservative default in the set.
  • The commitment is already in every visiting browser and is not undone by deleting the line.
  • Every subdomain anyone might visit must serve valid HTTPS, and a certificate error becomes a hard failure with no click-through.
  • Write the options out explicitly, so the promise is readable without knowing a framework default.
Lesson 1 of 7 · free

Keep going with lesson 2

How HSTS actually works

Explains the plain HTTP gap a redirect leaves, how browsers store HSTS rules, and the first-visit limit.

A free account also gives you the quizzes, your progress and notes, and the discussion.

Type to start searching...