0

I discovered after much painful debugging that requests to the the WP HTTP API (in this case, though wp_remote_request()) always ended up as GET method after being redirected, even if the method was something else in the initial request (in my case PURGE as used by the Varnish HTTP Purge plugin)

Normally this would apply to POST requests, where redirecting to a GET request of the same URL means completely obliterating the data being sent with the POST. In my case, using PURGE the outcome was that Apache was loading the actual URLs I was trying to purge, which wasn't what I wanted.

The point being: Pretty much never will you want this to happen. Whatever method you use to send a request, you surely want that method to be used in the end. This behavior is confusing and annoying and will probably be experienced as inexplicable bugginess for most users (in my case, the bug took me years to track down, and had been slowing down my local dev site where I don't have Varnish installed).

I'm posting this question so I can answer it myself in hopes people find it using Google in the future. The point is to be aware of this behavior so that if it is happening to you you can just find a way around it.

1

The change of method to GET after redirect from POST, PURGE, BAN whatever, is perfectly legal and been there, and will be without Wordpress (it has nothing to do with it, really). Welcome to the World Wide Web:

Note: For historical reasons, a user agent MAY change the request method from POST to GET for the subsequent request. If this behavior is undesired, the 307 (Temporary Redirect) status code can be used instead.

As a matter of rule, don't expect a method to be kept after redirect, unless you're able to modify the client software to do so.

  • Hi Daniel, you're wrong about WordPress but sort of right about 302. See my answer to myself. The WP_Http->browser_redirect_compatibility method explicitly enforces this conversion of all requests to GET when redirecting a request with a 302 code. If it weren't for that code in WP, this wouldn't happen when using the HTTP API, at least in my case. – jerclarke Jul 11 '18 at 18:58
  • Also very relevant is my note that this doesn't affect 301, so the obvious answer isn't to give up on your requests keeping their method when redirecting, but to make sure your web server uses 301 by default and/or when redirecting non-GET pages. – jerclarke Jul 11 '18 at 18:59
  • My point being, this happens in 99% cases, so I would mostly never expect method to be retained after a redirect. This is even being used for many patterns out there. Sorry it took you years to figure out and two lengthy posts in Stack Exchange to share your experience :) – Danila Vershinin Jul 11 '18 at 19:02
  • It won't happen if you use 301, as Nginx does in the same situation, where it works. Obviously you're generalizing when you say 99%, but for someone using the HTTP API in WP, this should be a 100% reproducible solution: Use 301 instead and it will work. – jerclarke Jul 11 '18 at 19:14
  • You're very right. I am generalizing. But if I don't, and go down to your particular problem, I can tell you simply misconfigured your Varnish instance. Proper BAN or PURGE requests should either result in 200 or 405, never a redirect. Should you have fixed it in the server, you wouldn't have WP to blame :) – Danila Vershinin Jul 11 '18 at 19:21
0

It turns out that as part of class-request->parse_response(), where redirections are handled for the various higher-level request functions (this is where it does the recursive requests for each subsequent hop) there's a weird hook thing that ends up running WP_Http->browser_redirect_compatibility() on the redirected request.

browser_redirect_compatibility() is pretty simple:

public static function browser_redirect_compatibility( $location, $headers, $data, &$options, $original ) {
        // Browser compat
        if ( $original->status_code === 302 ) {
            $options['type'] = Requests::GET;
        }
    }

Basically, it just obliterates the method you chose as soon as theres a 302 redirection, replacing it with GET. They seem to assume they are converting a POST request, but as is clear from reading it, this will apply to ANY method other than GET which ends up being a 302.

Notably though, this DOESN'T have any effect on 301 redirects, and seems to be related to an underlying crappiness of 302 as an error. You can read more about 302 and it's problems in [this previous question about a similar problem with POST requests getting converted to GET and losing their data]( Temporary redirect prevents getting $_POST array)

Affects Apache but not Nginx?

This seems relevant: My local dev environment uses MAMP and the sites is hosted through Apache, and this insane problem is triggered.

Our live site uses a Nginx->Varnish->Nginx setup, where for whatever reason, the same redirects were never 302 at all, but instead were 301 and thus worked correctly (they stayed as PURGE at the redirected location, which was the https version of the URL, rather than the http version).

So my recommendtaions to anyone having this problem:

  • Switch to Nginx completely as it seems to not use 302 for such redirects. Apache sux.
  • Look for sources of 302 errors and reconfigure your server to use 301 instead
  • Look for ways to avoid triggering redirects in the first place! (in our case, making sure the URLs were already https to start is an obvious improvement and will make our logs cleaner)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.