Block Proxy Visits with PHP
I wrote recently about how to block proxy visits with WordPress. That article provides a simple, plug-&-play script that you can drop into WordPress-powered site. This article goes further with two effective techniques for blocking proxy visits to your site using only a few lines of PHP.
These techniques work for any PHP-enabled site, including WordPress, Drupal, Joomla, and many others. And they’re both easy to implement. Just a few minutes and your site can be relatively free of most proxy visits.
Why block proxies?
Proxy services are used for all sorts of malicious activity. Anything that bad actors want to accomplish, if they are savvy they’re going to use some sort of a proxy to help obscure their true identity. Proxies are used for things like spamming, scraping content, cheating authentication, obscuring identity, and scanning for exploits. In general, most malicious activity is associated with some sort of proxy service. With that in mind, here are some good reasons why you would want to block proxies from accessing your website:
- Save valuable server resources like bandwidth and memory
- Ensure accurate user identification (i.e., registration, voting, et al)
- Secure your site against a wide range of malicious activity
So those are the main benefits, but there also is a big downside to consider. Decent folk (and software) use proxies for good instead of evil. For example, troubleshooting site connectivity issues is greatly facilitated when you can use a proxy service to access the site. And there are many other good ways to use a proxy; in many cases, the user simply wishes to remain anonymous, so they aren’t tracked, ad-targeted, and so forth. Additionally, many ISPs send customer traffic through a proxy server. So with that in mind, here are the reasons why you would NOT want to block proxies:
- May end up blocking some legit proxy users
- Slight performance hit required for certain proxy-blocking techniques
So basically it all boils down to how many of your legit visitors (including apps, etc.) are using a proxy to access your site. If minimal, then the potential benefits may outweigh the downsides. Totally your call, just want you to be aware of both sides of the equation. It’s also possible to take the middle ground and block proxies conditionally, by evaluating other aspects of each HTTP request and acting accordingly.
Can’t block them all
Here is something else to consider when deciding whether or not to block proxy servers. It’s impossible to accurately block all proxies with a simple code snippet. Proxy technology continues to evolve quickly in terms of functionality, complexity, and effectiveness. So there’s not really a one-size-fits-all, plug-&-play way to block 100% all proxies. That said, the techniques covered in this article provide simple, cost-effective, easy-to-implement solutions for blocking a wide range of proxy services (but not all of them).
Block proxy visits via HTTP headers
Most transparent proxies identify their functionality by sending a proxy header along with each HTTP request. This means that we can block a wide range of proxy visits by checking HTTP headers and denying access to anything that claims to be a proxy. Here is how we can achieve this using a simple PHP function:
function shapeSpace_block_proxy_visits() {
$headers = array('CLIENT_IP','FORWARDED','FORWARDED_FOR','FORWARDED_FOR_IP','VIA','X_FORWARDED','X_FORWARDED_FOR','HTTP_CLIENT_IP','HTTP_FORWARDED','HTTP_FORWARDED_FOR','HTTP_FORWARDED_FOR_IP','HTTP_PROXY_CONNECTION','HTTP_VIA','HTTP_X_FORWARDED','HTTP_X_FORWARDED_FOR');
foreach ($headers as $header){
if (isset($_SERVER[$header])) {
die('Proxy access not allowed.');
}
}
}
This function begins by defining a list of known proxy headers. It then loops through the list to check if any of the headers were sent with the request. If so, then the request is denied via die()
and a simple message.
The downside to this technique is that not all proxies send headers that identify themselves as such. So while this is one of the better solutions performance-wise, it’s not as effective as the next technique, which is much better at blocking the more anonymous proxy services.
Block proxy visits via port scanning
This second proxy-block technique works by scanning for commonly used ports on the remote machine. If one of the ports is found open, most likely the request is routed via proxy server. Here is an example of this strategy, implemented via simple PHP function.
function shapeSpace_block_proxy_visits() {
$ports = array(80,81,553,554,1080,3128,4480,6588,8000,8080);
foreach ($ports as $port) {
if (@fsockopen($_SERVER['REMOTE_ADDR'], $port, $errno, $errstr, 5)) {
die('Proxy access not allowed.');
}
}
}
Because it doesn’t rely on the proxy header information, this solution is more effective than the previous technique. The downside here is that scanning for open ports requires more time than simply checking for HTTP headers. So keep an eye on your site’s performance if implementing this code. FYI, this is an improved version of the technique provided in my tutorial, Block Tough Proxies.
Note regarding scanning: best practice dictates that you only scan each IP address only one time. To accomplish this, you could adapt the previous script to store scan results for each IP address in the database, and then check for existing IPs before doing each scan. This strategy helps keep visitors happy and also optimizes for performance.
Update: send response headers
If you are getting incorrect response headers (e.g., getting 200 “OK” for 404 “Not Found” pages). You can resolve by sending the desired header, for example to send 403 “Forbidden” response for any blocked proxy requests:
function shapeSpace_block_proxy_visits() {
$headers = array('CLIENT_IP','FORWARDED','FORWARDED_FOR','FORWARDED_FOR_IP','VIA','X_FORWARDED','X_FORWARDED_FOR','HTTP_CLIENT_IP','HTTP_FORWARDED','HTTP_FORWARDED_FOR','HTTP_FORWARDED_FOR_IP','HTTP_PROXY_CONNECTION','HTTP_VIA','HTTP_X_FORWARDED','HTTP_X_FORWARDED_FOR');
foreach ($headers as $header){
if (isset($_SERVER[$header])) {
header('HTTP/1.1 403 Forbidden'); // send response header
die('Proxy access not allowed.');
}
}
}
You can change the header to whatever response, etc. Check the PHP documentation for more details.
Going further
If you’re getting into crafting solutions for blocking proxy visits, here are some useful resources for going further:
- About DNS-based Blackhole Lists
- Spam and Open Relay Blocking System
- Comparison of DNS blacklists
Also, if your site supports .htaccess, you can get better performance by blocking proxies at the server level. Here are a couple of tutorials with more information:
5 responses to “Block Proxy Visits with PHP”
I have tried to add these two PHP functions into functions.php of my WordPress site but they do not block anything. Same time solution described here works fine.
Thanks to author anyway! Always useful ideas.
Hi Alex, are you just copy/pasting the code as-is? Because you also need to call the function, otherwise it won’t do anything.
Hi Jeff, Thanks. I have added the function from your previous post:
add_action('after_setup_theme', 'shapeSpace_block_proxy_visits');
It works but not in the way I expected :)
When I use port scan code it hangs my website for 30-40 secs consuming too much resources.
When I use HTTP header check code it blocks access to website wrongly identifying my visit as access via proxy. I have tried via home internet line and cellular 3G – both had the same result.
Therefore, I have to remain on the previous solution.
Yeah if you’re using WordPress, it makes sense to use the WordPress technique from the other, WP-specific article ;)
This article does explain about the port scan requiring extra time. How much so will depend on the robustness of your server resources, as well as other factors.
Likewise for the header-check technique, the article clearly warns about false positives, ISPs, et al. Most likely your ISP is routing your traffic thru a proxy. To check, you can look at the headers sent when you request any web page. You should see one or more of the headers listed in the article.
Thank you. It’s clear now.