I'm developing a REST API that may take in private information. To
make it secure, I want to have the API only accessible using HTTPS.
Basically, what is the best way to enforce HTTPS only?
If you're doing an API, the best way is to turn off HTTP and only publish an HTTPS interface.
- Down-level HTTP is a crutch for puny humans. If you're running a UI site, then you have to expect humans will try to go to the unencrypted HTTP version, because they're like that - that's why we have so many fancy ways of redirecting them. With an API, you can demand people code to HTTPS, and simply not provide HTTP, because it isn't users with browsers specifying your address.
- Puny humans at least have browsers working for them, which try to take into account all the neat things you've mentioned (status codes, headers). When an API programmer slaps together an API client, they'll often ignore or be creative with things like that. The 8 lines of RESTful Python they pulled off of StackExchange doesn't emphasize error handling or RFC-compliant header hewing. You simply can't trust that they'll respond to HTTP protocol and header hints and redirections.
- APIs are less likely to shove unencrypted input over your TCP if you're running HTTPS... but it's not impossible. (Their first packet after the 3WHS could be a data-laden JSON request instead of a TLS CLIENT_HELLO... but if they never get a response back (and they won't if they do that) they'll learn to stop sticking their finger that socket soon enough.) The lesson here is, again, please don't trust API client programmers to do the right thing. And never reward them ignoring encryption with a legitimate response, even a 403 Forbidden (as they might get if you support HTTP).
- Set the HTTP status code to 403 Forbidden
- Send the Strict-Transport-Security header
- Redirect to the HTTPS URL using the Location header
These are all excellent things if you have a HTTP site and want to push humans bearing browsers over. But when dealing with an API, instead of a browser, it's a set of assumptions that are less likely to be true and less necessary in the first place.