Back to blog
Building a Proxy Forwarder with 3Proxy, Squid & Laravel
7 min read

Building a Proxy Forwarder with 3Proxy, Squid & Laravel

Want a quick summary of this post? Tune in 🎧
Table of Contents

Recently I needed to re-route, transform, and redirect proxies. The challenge was: how could I hide my real proxy behind another proxy by redirecting requests? Or could I set up a simple authentication method? In this article we’ll cover:

Key Concepts & Terminology πŸ“š

  • Inbound Port β€” Port that accepts incoming traffic, typically used on your device (ex: 192.168.111.2:8080)
  • Outbound Port β€” Port that redirects traffic to the destination server
  • Inbound IP/Domain β€” The IP or domain that receives connections from your device
  • Outbound IP/Domain β€” The destination IP or domain that receives the redirected traffic
  • Template Variables β€” Wildcards like {{outbound_port}} or {{xxxx}} that get replaced dynamically by scripts (PHP str_replace) with actual ports and IPs

Architecture Overview πŸ—οΈ

The diagram below shows how we mask the original proxy by creating a forwarding proxy that redirects traffic to the real proxy server.

Diagram showing how a proxy forward works

Squid Proxy Setup πŸ¦‘

What is Squid?

Squid is one of the most widely used and reliable proxy solutions. It provides proxying, caching, authentication, user groups, and more. I didn’t have time to explore all features, but at least it’s not made by Russians! πŸ˜„ (joking of course).

Installation Guide: Learn how to install Squid here

Documentation for Squid can be sparse online. What I do know is that Squid relies on a single configuration file:

Squid Configuration Template πŸ“

Here’s a sample Squid configuration file showing how to set up a proxy. You can find more about the configuration here.

The key difference is this template uses placeholders that Laravel (or any language) can replace dynamically:

header_replace Accept-Encoding gzip
auth_param basic program /usr/lib/squid/basic_ncsa_auth /etc/squid/passwd
auth_param basic credentialsttl 999 minutes
auth_param basic casesensitive on
auth_param basic realm easyshit
acl ncsa proxy_auth REQUIRED
follow_x_forwarded_for deny all
forwarded_for delete
via off

# Proxy Redirect Line
{{lines}}

# Squid Default Config
acl SSL_ports port 443
acl Safe_ports port 80		# http
acl Safe_ports port 21		# ftp
acl Safe_ports port 443		# https
acl Safe_ports port 70		# gopher
acl Safe_ports port 210		# wais
acl Safe_ports port 1025-65535	# unregistered ports
acl Safe_ports port 280		# http-mgmt
acl Safe_ports port 488		# gss-http
acl Safe_ports port 591		# filemaker
acl Safe_ports port 777		# multiling http
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localhost
http_access allow ncsa
coredump_dir /var/spool/squid
refresh_pattern ^ftp:		1440	20%	10080
refresh_pattern ^gopher:	1440	0%	1440
refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
refresh_pattern (Release|Packages(.gz)*)$      0       20%     2880
refresh_pattern .		0	20%	4320

Single Proxy Redirect Configuration

Here’s the configuration for a single proxy redirect line:

# Authentication for User with Port {{inbound_port}} with Reference #{{reference}}
# The user is being routed to {{outgoing_ip}}:{{outgoing_port}}
{{permissions}}
http_port {{inbound_port}} name=port_{{inbound_port}}
acl port_{{inbound_port}}_acl myportname port_{{inbound_port}}
always_direct deny port_{{inbound_port}}_acl
never_direct allow port_{{inbound_port}}_acl
cache_peer {{outgoing_ip}} parent {{outgoing_port}} 0 no-query {{outgoing_auth}} name=proxy{{inbound_port}}
cache_peer_access proxy{{inbound_port}} allow port_{{inbound_port}}_acl
cache_peer_access proxy{{inbound_port}} deny all
http_access allow localhost auth

More information can be found on the Squid Documentation page, but I found it confusing, so I searched extensively to get this proper config. Thanks also to some friends in the proxy business who lent me a hand πŸ™‚.

Note: This configuration uses wildcards that will be replaced dynamically.

3Proxy Setup βš™οΈ

How 3Proxy Works

Unlike Squid, 3proxy runs as a simple bash process that serves as our connection listener. A process runs on the machine as the connection listener. When we no longer need it, we simply kill the process and the proxy/routing stops - this reduces management overhead significantly.

The examples below show:

  1. HTTP to HTTP proxy
  2. HTTP to SOCKS5 proxy

Remember: wildcards in {{}} format get replaced with actual values via PHP scripts.

Installation Guide: Learn how to install 3proxy here

Simply run/kill this process to start/stop proxy routing.

3Proxy Configuration Template πŸ“„

#!/bin/bash

# Subscription: {{ $SUBSCRIPTION_REFERENCE }}
# Proxy: {{ $PROXY_REFERENCE }}
# Allocated port: {{ $ALLOCATED_PORT }}
# The user is being routed to {{ $ORIGINAL_HOST }}:{{ $ORIGINAL_PORT }}

daemon
pidfile /var/run/{{ $PID }}.pid
nserver {{ $PROXY3_DNS_1 }}
nserver {{ $PROXY3_DNS_2 }}
nscache 65536
nscache6 65536
timeouts 1 5 30 60 180 1800 15 60
deny * * 127.0.0.1,192.168.1.1
# Admin for this proxy, should be common for all users
auth strong
users {{ $PROXY3_ADMIN_USERNAME }}:CL:{{ $PROXY3_ADMIN_PASSWORD }}
allow {{ $PROXY3_ADMIN_USERNAME }} * * *
flush

# Build the user line
maxconn {{ $MAX_CONNECTIONS }}
auth strong
users {{ $ALLOCATED_USERNAME }}:CL:{{ $ALLOCATED_PASSWORD }}
allow {{ $ALLOCATED_USERNAME }} * {{ $ALLOWED_DOMAINS }} 80-88,8080-8088,443 HTTP,HTTPS
parent 1000 {{ $ORIGINAL_PROTOCOL }} {{ $ORIGINAL_HOST }} {{ $ORIGINAL_PORT }} {{ $ORIGINAL_USERNAME }} {{ $ORIGINAL_PASSWORD }}
proxy -4 -olSO_REUSEADDR,SO_REUSEPORT -ocTCP_TIMESTAMPS,TCP_NODELAY -osTCP_NODELAY -n -a -p{{ $ALLOCATED_PORT }}
flush

Auto-Start Script πŸ”„

A small bash script can automate the process startup. The {{proxy3_bin}} is the path to your 3proxy binary and the {{proxy_config}} is the path to your .conf file (mentioned above). We also call Laravel Artisan command to verify the proxy is still valid. Our backend handles the necessary commands to delete config files and terminate processes.

#!/bin/sh
if ps -aux | grep "3[p]roxy" | grep "{{proxy3_bin_grep}} {{proxy3_config}}" > /dev/null 2>&1;
then
echo 'Running {{reference}}';
else
{{proxy3_bin}} {{proxy3_config}} &
fi
/usr/bin/php -f {{artisan}} check:order {{reference}}

Laravel Helper Class πŸ”§

Squid & 3Proxy Helper

The following PHP Class is just a helper I’ve created to automatically update/generate config files on demand (from database in this case). This doesn’t work out of the box and requires additional configuration. I’m providing just a proof of concept, not the complete code.


/**
  * Replaces the keys and generates the user rules
  *
  * @return mixed
  * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
  */
private function getProxyConfig(): string
{
    $keys = [
        # Inbound
        '{{inbound_port}}' => $this->getInbound()->getPort(),
        '{{inbound_username}}' => $this->getInbound()->getAuthUsername(),
        '{{inbound_password}}' => $this->getInbound()->getAuthPassword(),
        '{{inbound_allowed_urls}}' => '*', // Comma delimited instagram.com,*.instagram.com
        # Outbound
        '{{outgoing_ip}}' => $this->getOutbound()->getIpOrHostName(),
        '{{outgoing_port}}' => $this->getOutbound()->getPort(),
        '{{outgoing_protocol}}' => $this->getOutbound()->getProtocol(),
        # Other
        '{{pid}}' => random_number(100000, 10000000),
        '{{max_connections}}' => 10000,
        '{{name_server_1}}' => config('app.proxy3-dns-server-2'),
        '{{name_server_2}}' => config('app.proxy3-dns-server-2'),
        '{{reference}}' => $this->reference,
        '{{admin_username}}' => config('app.proxy3-admin-username'),
        '{{admin_password}}' => config('app.proxy3-admin-password'),
    ];

    # Check if there is auth
    if ($this->outbound->hasAuthentication() && $this->outbound->getAuth() !== null) {
        $keys['{{outgoing_username}}'] = $this->outbound->getAuthUsername();
        $keys['{{outgoing_password}}'] = $this->outbound->getAuthPassword();
    } else {
        $keys['{{outgoing_username}}'] = '';
        $keys['{{outgoing_password}}'] = '';
    }
    if($this->inbound->getProtocol() === Parser::PROTOCOL_SOCKS5){
        return replace($keys, self::getConfigTemplateSocks5());
    }
    return replace($keys, self::getConfigTemplate());
}

The Result πŸŽ‰

Here’s a small example of an internal Laravel app we built to manage everything. Keep in mind that some code might NOT be optimized as this is a demo project for the sole purpose of testing 3proxy and squid. Feel free to enhance the database logic, helpers, and documentation. If you have any questions feel free to ask!

Proxy Manager Dashboard

Conclusion 🎯

We’ve learned how to:

  • Use Squid & proxy config files (more or less), setup, and how it works
  • Build simple proxy redirecting using 3proxy from HTTP β†’ HTTP and HTTP β†’ SOCKS5
  • Automate all this logic using a back-end (Laravel & PHP)

Proxies can be used for numerous reasons such as: keeping your connection safe, bypassing blocked websites in your country, crawling, etc! If you liked this article please clap or comment below!

Like this post? Sharing it means a lot to me! ❀️