WordPress failing to redirect with protected Posts

I recently had a fairly obscure problem on a WordPress site and I wanted to write about it on the off chance it saves somebody else a few frustrating hours.

The problem

Whenever a password was entered into a protected posts password box the form would submit successfully, start to redirect, then die on a blank screen. If the webpage was refreshed the password protected post was unlocked (indicating it was likely a redirect problem not an auth problem).

The Cause

It turns out that WordPress ends up using a helper function called wp_get_raw_referer() to retrieve the address of the previous page. If we take a look at it we see it first checks for a WordPress custom variable called ‘_wp_http_referer’ then the default $_SERVER variable ‘HTTP_REFERER’ before retiring false if neither are set. All perfectly reasonable.

How this affects password protected posts?

The default WordPress password form on posts doesn’t contain the custom ‘_wp_http_referer’ field meaning WordPress is always relying on ‘HTTP_REFERER’ for redirecting. The problem with this is that ‘HTTP_REFERER’ is famously unreliable as it’s set by the user agent, meaning it can be wrong or even missing altogether, as in my case. When navigating from an HTTPS to HTTP page web browsers don’t set the ‘HTTP_REFERER’ field for ancient security reasons (there is a work around in modern browsers involving meta tags). So, as the password protected page redirects you through the WordPress login page to handle the form, if you’re WordPress admin is behind SSL (‘FORCE_ADMIN_SSL includes the login page) but your front-end isn’t (because you’re serving ads or whatnot) the redirect on the password protect page dies on a blank screen as the ‘HTTP_REFERER’ field is stripped out after the wp-login page.

The Solution

Luckily, the fix is easy. We just need to add the ‘_wp_http_referer’ field to the password protect form then we don’t rely on ‘HTTP_REFERER’ at all. Simply add this to your functions.php file and it will do just that.

/**
 * Add a redirect field for password protect forms when
 * HTTP_REFERER isn't set
 *
 * @access public
 * @param string $form_html
 * @return string
 */
public function wpa_add__wp_http_referer( $form_html ) {
 if ( empty( $_SERVER['HTTP_REFERER'] ) ) {
	 $form_html = str_ireplace( '</form>', '<input type="hidden" name="_wp_http_referer" value="' . esc_url( get_the_permalink() ) . '"></form>', $form_html );
 }

 return $form_html;
}
add_filter( 'the_password_form', array( $this, 'wpa_add__wp_http_referer' ), 10, 1 );

It’s fairly obscure I admit but hopefully it will help someone and save them a few hours debugging!