It’s a pattern that has haunted me my entire career. It starts like this:
- Encounter technical problem.
- Search in vain for an answer.
- Lose all hope.
I once again stumbled through this sequence while I was puttering around with my Last Tweet in AWS Twitter shitposting client recently. Allow me to take you on a little journey through my painful experience.
Corey encounters a technical problem
In case you hadn’t heard of it before, Last Tweet in AWS is a free-to-use Twitter client that makes creating threads easier while providing passable image recognition + OCR to create alt-text for images. It’s built atop serverless technologies, and it’s served from 20 AWS regions, depending on which is the closest to the user.
Because it uses AWS’s API Gateway v2, or “HTTP APIs,” it only listens on port 443 via TLS. That service will not listen on port 80. This is fine; I don’t want people passing sensitive data in plaintext! The problem I run into is when I forget to explicitly put an HTTPS in front of the domain, then a service (such as, amusingly, Twitter) attempts to direct traffic to port 80 instead whenever anyone clicks the link. When this happens, the connection attempt fails, and it appears to all the world as if there’s no website working at the domain in question.
There’s no great answer for this that doesn’t dramatically overcomplicate things. The official AWS answer is to go ahead and slap CloudFront in front of the site. Great; genius plan. That would mean that I’d need to either configure 20 origins for the CloudFront distribution and find some way to get them to pick the closest one to the user or else undo the benefit of having a multi-region setup. Every request is dynamic, so there’s no caching story that makes sense.
This was the state of things when I found the one answer that at least somewhat mitigates the concern. In 2012, a standard was released called HTTP Strict Transport Security. While fairly complicated (because this is the internet, after all), it states that a website can return a header named Strict-Transport-Security
that specifies a length of time. Browsers remember that and, for the length of that time, refuse to even attempt to connect to the site over an unsecured connection.
While that works well for folks who have visited the site before, a combination of traffic analysis, a careful counting of the number of commas in my AWS bill, and basic arithmetic all indicate that far more people have not visited Last Tweet in AWS than have done so before — so what good does this do for 99.9999% of the internet’s population?
This is where the HSTS Preload List comes into play. This is a list of domains that return the proper header, and it’s periodically loaded into all major web browsers. Once a domain is added to this list and circulated to the browser providers, this problem gets solved for basically everyone since the browser will fail to even attempt to connect on port 80 — though the process does take months.
My problem, and the reason for this post, was in figuring out how to set a static header on the v2 HTTP API.
Corey searches in vain for an answer
A few things make this a very hard thing to research online. “API Gateway” is a common term. “HTTP API” is such an incredibly overbroad term across all manner of vendors as to be effectively impossible to search for.
Most combinations of search terms that I tried, both on Google and (begrudgingly) on AWS’s own documentation search dingus, led me to the same narrow subset of search results. Now, I’m no $1.434 trillion company, but it seems to me that if you’re running any form of analytics on your documentation traffic, and you see the same user arriving at the same page repeatedly over the course of an hour via a variety of paths, perhaps this might be signal that they’re looking for something that they aren’t finding. I abhor popups when I’m trying to get something done, but the fifth time I’m staring at the same page, wondering what it is I don’t understand, that would be a great time for the documentation to offer to help me more effectively.
Corey nearly loses hope
I finally found the answer via a carefully constructed code search on GitHub, which shouldn’t have been necessary but absolutely worked out in the end.
For the record, the answer to my question about implementing a static header for HSTS is “whatever is returning data to the API Gateway, have it insert an arbitrary header into that response and the API Gateway v2 will pass it through without comment” … but that was basically impossible to discover.
To set a static header on the v2 HTTP API to enable HSTS, I added the line res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
. (Honestly, making this solution discoverable via Google is half of the reason I’m writing this blog post.)
Solving for the impossible search process
The larger problem is that when we’re faced with a technical challenge, “how do I do this” is the burning question that looms large in all of our minds. We search and we plumb the depths of forum threads that haven’t been updated in a decade, in pursuit of the magic answer that sorts us out. It eludes us, drives us slowly mad — and then we figure it out!
See, there are five steps to this harrowing pattern for developers:
- Encounter technical problem.
- Search in vain for an answer.
- Lose all hope.
- Eventually discover an answer.
- Don’t bother to publish the answer for the next sucker.
Here’s the problem: None of us goes back to all of those dead ends to update the long-forgotten forum posts. We don’t return to Stack Overflow and update old questions (they’d just get closed as Off-Topic anyway). We don’t hit the surprisingly effective “Feedback” button on the footer of every AWS documentation page.
This is the problem I don’t have an answer for, and it’s haunted me my entire career. If you have an idea to solve it — and it doesn’t involve paying some sketchy startup a pile of money for unclear value — please reach out to share it with me.
Because if not, then you can more or less expect a blog post like this one so that the next poor sot who tries to set a static header on an AWS v2 API Gateway to enable HTTP Strict Transport Security can find the answer easily enough — after reading through the rest of this screed.