How to Prevent Direct Access to Specific URL in WordPress – NO PLUGIN
Use Case
The client wanted to be able to advertise available positions directly on their website. They also wanted people to be able to apply directly for the jobs on their website without having to go to another site, like Indeed or LinkedIn.
They didn’t give me much more than that. But to me, that meant that each position should have a button linked to the simple application form. This form would receive the position the applicant is applying to and place that in a hidden field on the form. When the applicant submitted the completed form, the HR person would receive all of the relevant information to reach out to the applicant.
So I created a plugin that added a custom post type – Careers – created the template for the post and the contact form. Easy-peasy, until I realized that the form could be accessed directly by URL.
This doesn’t seem like much of a problem. Applications submitted through this form without the Position attached would require the person responsible for contacting applicants to ask more questions. Bu, my goal was to make the solution as efficient and automated as possible, to save those busy folks some time. So, it may not be a big deal, but it isn’t a complete solution.
The Solution
UPDATE: I’ve further streamlined this function. I still check if the referer exists or not; but I now perform more checks on the referer. I test if it has a “position” in the referer, then I validate that the “position” is actually from our site to prevent referer spoofing. Check out the gist to get the full function. Customize it for your needs.
Searching “prevent direct access to specific URL WordPress” sent me all over Google, the WordPress Developer Handbook, and Stack Exchange. I found some good insight, here or there, that I used to put together for a complete solution.
I had to piece it together because the answer involves wp_get_referer()
which you would use to get the HTTP referer variable from the server request. Most answers I saw were for cases where the referring URL is the same each time. I couldn’t use those answers because the referring URL would always depend upon which position the applicant came from. So I ended up using some pure PHP functions, specifically parse_URL
and strpos
, to help out. Maybe you’re in the same position. I hope this helps.
We start with the function – you can place this in your functions.php file. I went ahead and defined the redirect URL first. If visitors accessed the URL directly, without knowing their intention, I felt it was best to redirect them to the Careers page where they can choose a position to apply for…the first logical step.
function dtl_no_applicant_direct_access() {
// Define Careers Page URL for redirect.
$careers = get_permalink( get_page_by_title( 'Careers' ) );
Next, we figure out if we’re on the right page. We only want to apply this redirect to the Apply Now page, so we’ll define that action here.
// If this is not the Apply Now page, do nothing.
if( ! is_page( 'Apply Now' ) ) {
return;
}
Now let’s get our referer from the server request, using the wp_get_referer()
function. Our positions have a path to them like – https://www.example.com/position/job-title – so we need to separate the additional information from the URL. So we parse it with the PHP function parse_URL()
.
Not so fast. When I first posted this, I was so concerned with external direct URL access. But, what about access from the admin dashboard? Before we get the referer, let’s make sure administrators and those with the correct permissions can edit this page.
// Allow direct access to logged in users who can edit pages.
if( is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
return;
}
//Get the referrer from the server request and parse the URL.
$referer = wp_get_referer();
$refer_URL = parse_URL( $referer );
Now that we have our URL parsed, let’s check it for the path we expect in the referer. We declare a variable to hold the results of the strpos()
function, which is checking the haystack – $refer_URL[ 'path' ]
– for the needle – 'position'
.
//Check for Position in the referrer path.
$position = strpos( $refer_URL[ 'path' ], 'position' );
I placed the referer and the parser in two separate variables for a specific reason. While performing some unit tests I noticed that the strpos()
function would not return anything if $referer
did not exist – there’s nothing to parse. To simply check for the path – TRUE
or FALSE
– the page could still be accessed because the return condition would never be FALSE
or TRUE
.
To overcome this, I check for two conditions. If the referer is valid – the request could have a different referer if the URL was posted online somewhere. If the request has a referer at all – it would not have a referer if the URL was entered into the address bar. I want to redirect in both cases.
// If referrer path is from a Position, do nothing. This is a valid application.
if( $position > -1 ) {
return;
// Otherwise the user did not come from a Position. Redirect to previous page or Careers.
} elseif( !isset( $referer ) || $position === FALSE ) {
nocache_headers();
wp_safe_redirect( $careers );
exit;
}
} add_action( 'template_redirect', 'dtl_no_applicant_direct_access' );
There’s something to note in those last bits. HTTP redirect responses are cached. Following the guidance of a comment on the wp_safe_redirect()
function, I added nocache_headers()
before the redirect to prevent the response for requests to this page from being stored.
I used wp_safe_redirect()
instead of wp_redirect()
because it validates the redirect request before proceeding. This is a good security practice to prevent user agent redirects. Lastly, always call exit;
after wp_safe_redirect()
or wp_redirect()
and don’t forget to call your function with template_redirect()
.
Was this helpful? Do you have a more efficient way to implement this? Leave me a comment, maybe it will help someone out.