How can a POST request become a GET?

August 13th 2021 HTTP .NET

Recently I solved a problem with a POST request that failed with a 405 error (Method Not Allowed), even though there was only a POST endpoint at the requested URL. After solving the problem, I found it interesting enough to reproduce it and wrote a post about it.

The endpoint could be any POST endpoint in an ASP.NET Core Web API project (or any other framework):

[HttpPost]
public IEnumerable<WeatherForecast> Post()
{
    var rng = new Random();
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = rng.Next(-20, 55),
        Summary = Summaries[rng.Next(Summaries.Length)]
    })
    .ToArray();
}

The client-side code can be a simple HttpClient request if you use .NET:

var http = new HttpClient();
var response = await http.PostAsync(
    "http://post2get.azurewebsites.net/WeatherForecast",
    null);

The key ingredient is Azure App Service hosting with the HTTPS Only option enabled.

This caused the service to return 301 (Moved Permanently) responses to HTTP requests. As documented, some clients don't keep the same method and body:

Even if the specification requires the method (and the body) not to be altered when the redirection is performed, not all user-agents align with it - you can still find this type of bugged software out there.

Obviously, the .NET HttpClient is one of them. It performs a GET request after the redirect. However, it took me a while to figure that out, though. And it would have taken me even longer if it weren't for the network logging available in both .NET Core and the .NET framework.

The problem was easy to solve after that. I changed HTTP to HTTPS in the request URL. In my case, it was read from a configuration file, so I didn't even have to change any code to make it work.

Interestingly, I couldn't reproduce the problem locally even when I configured the ASP.NET Core application to redirect to HTTPS:

app.UseHttpsRedirection();

The difference was that the application sent a 307 (Temporary Redirect) response to HTTP requests, which caused the HttpClient to keep the same method and body in the redirected request.

I've pushed the code I wrote to reproduce the problem to my GitHub repository. To verify the redirection behavior of Azure App Service, you'll need to publish it there yourself. There's a free plan for up to 10 services.

Relying on HTTPS redirection in your code may lead to unexpected results. It's best to use the HTTPS endpoint directly if possible. Network logging on the client can be a useful tool for troubleshooting unexpected behavior when invoking REST services.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License