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 redirecting1. 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 target2. 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