Recently I once again stumbled upon a detail of the HTTP specification involving the POST verb and "enforced" https. I'll document it here in an attempt to save other people the time (hopefully also future-me).
I put "enforced" in quotation marks because it just means to also listen on port
80
- apart from listing on port 443
for the encrypted traffic - and issue a redirect (301
) to the same URL but with https
instead of http
as protocol. The thing is that different clients handle that redirect differently and especially the Python requests
library may confuse you - at least it did confuse me: Say you send a POST
request to a http
URL and the server issues a HTTP response code of 301
. According to Wikipedia, the client should confirm with the user before redirecting ((https://en.wikipedia.org/wiki/HTTP_301)). But on the command line or - even worse - in case of libraries used for programming, this is not (easily) possible.
The situation is even more involved if one uses the HTTP status code 302
(which one should *not* do for enforcing https btw): In these cases some - but not all - clients translate a POST
request to a GET
request and use that verb against the redirection target ((https://en.wikipedia.org/wiki/HTTP_301)). The two HTTP status codes 303
and 307
have been introduces to deconflict this but all participants need to be backwards-compatible of course.
Anyhow, let's use your favorite CL tool
curl -v -L -X POST 'http://example.com' > /dev/null
wget -O- --method=POST 'http://example.com' > /dev/null
or the Python requests
library to perform a POST
request:
import requests
requests.post('http://example.com')
All three tools don't interact with the user in any way. They especially re-perform a POST
in case of 301
without asking first which seems to me like a straight violation of the specification. I cut requests
some slack of course: As a library it should not wait for user-input during execution.
Tool | 301 redirect | 302 redirect
-----------|--------------|----------------
curl
| keep POST
| keep POST
wget
| keep POST
| switch to GET
requests
| keep POST
| switch to GET