1. Introduction
1.简介
When it comes to Nginx, it’s one of the most popular servers out there. It’s fast, lightweight and responsible for hosting some of the biggest sites on the internet. Nginx is often used as a load balancer, a reverse proxy, and an HTTP Cache, among other uses.
说到Nginx,它是目前最流行的服务器之一。它速度快,重量轻,负责托管互联网上一些最大的网站。Nginx经常被用作负载平衡器、反向代理和HTTP缓存,以及其他用途。
In this tutorial, we are focusing on learning how to use it as a forward proxy for any requested location.
在本教程中,我们将重点学习如何将其作为任何请求地点的前向代理。
2. The Motivation for a Forward Proxy
2.前向代理的动因
Proxy servers are entities that act as middlemen between a client and the host of the requested resource. This means the traffic goes through an additional machine in order to get to the destination (host server). The proxy continues the request on behalf of the client, so when the host server accepts the request, they only see the IP of the proxy. In comparison, a reverse proxy sits directly in front of the web and routes the request coming from the client to the correct web server (inside a network of multiple servers).
代理服务器是在客户和被请求资源的主机之间充当中间人的实体。这意味着流量要通过一个额外的机器才能到达目的地(主机服务器)。代理服务器代表客户继续请求,所以当主机服务器接受请求时,他们只看到代理服务器的IP。相比之下,反向代理直接坐在网络前面,将来自客户端的请求路由到正确的网络服务器(在多个服务器的网络内)。
The only downside of using forward proxies is that they work on the application level, so we have to set up the proxy for each app we’re planning to route the traffic for.
使用前向代理的唯一缺点是,它们在应用层面上工作,所以我们必须为我们计划路由流量的每个应用设置代理。
Some use cases for using a Forward Proxy are:
使用转发代理的一些用例是。
- Masking the IP and location to gain access to location-restricted services
- For isolated internal networks that need to connect to specific resources on the internet
- For caching requests to specific servers for content that rarely changes in order to save resources
It’s worth noting that proxies do not encrypt traffic, whereas VPNs redirect the traffic through secure and encrypted tunnels.
值得注意的是,代理不对流量进行加密,而VPN则通过安全和加密的隧道重定向流量。
3. Implementing a Forward Proxy with Nginx
3.用Nginx实现一个转发代理
In order to implement a forwarding proxy, we’re going to use a Linux machine with Nginx installed. For the sake of this tutorial, we’ll be using VirtualBox with a Linux distro server that’s up and running, together with Nginx installed, but you can use whatever is more convenient to you like Docker or even the old PC that’s been lying in the corner for years.
为了实现转发代理,我们将使用一台安装了Nginx的Linux机器。在本教程中,我们将使用安装了Nginx的Linux发行版服务器的VirtualBox,但你可以使用任何对你来说更方便的东西,如Docker,甚至是多年来一直躺在角落里的旧电脑。
First, we locate the default Nginx configuration file and comment out the server part in order to save it as an archived copy. Usually, we can find it in /etc/nginx/sites-enabled/default:
首先,我们找到默认的Nginx配置文件,注释掉server部分,以便将其保存为一个存档副本。通常,我们可以在/etc/nginx/sites-enabled/default中找到它。
# Default server configuration
#server {
#listen 80 default_server;
#listen [::]:80 default_server;
#root /var/www/html;
# Add index.php to the list if you are using PHP
#index index.html index.htm index.nginx-debian.html;
#server_name _;
#location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
#try_files $uri $uri/ =404;
#}
#}
Next, let’s create a new file called forward and add all the needed configurations to turn Nginx into a working forward proxy:
接下来,让我们创建一个名为forward的新文件,并添加所有需要的配置,将Nginx变成一个有效的前向代理。
server {
listen 8888;
location / {
resolver 8.8.8.8;
proxy_pass http://$http_host$uri$is_args$args;
}
}
With the first configuration ‘listen 8888;’ – we’re basically telling the server that all the requests going to this port must be handled with the following configurations. The location argument is responsible for specific server sub-division block configurations and basically tells the server how to handle requests for specific URIs.
通过第一个配置‘listen 8888;’ –我们基本上告诉服务器,所有去这个端口的请求都必须用以下配置来处理。location参数负责具体的服务器分块配置,基本上告诉服务器如何处理特定URI的请求。
The ‘resolver 8.8.8.8′ directive specifies which nameservers should be used to resolve the names of upstream servers into addresses, in this case 8.8.8.8 corresponds to Google’s nameservers.
‘resolver 8.8.8.8’指令指定了应该使用哪些名称服务器来将上游服务器的名称解析为地址,在这种情况下,8.8.8.8对应于Google的名称服务器。
The variable $http_host contains the host in the original request, whereas $uri contains the path after the domain or IP. The last two variables $is_args and $args check for any additional arguments in the initial request, and they add them automatically to the proxied request.
变量$http_host包含原始请求中的主机,而$uri包含域名或IP之后的路径。最后两个变量$is_args和$args检查初始请求中的任何附加参数,它们会自动添加到代理的请求中。
After we update all the necessary configurations, we need to restart the nginx.service for them to take effect:
在我们更新了所有必要的配置后,我们需要重新启动nginx.service,使其生效。
sudo systemctl restart nginx.service
4. Using the Forward Proxy
4.使用转发代理
As we mentioned before, forward proxies work on the application level, so naturally, depending on the client, there are multiple ways that we can configure the forward proxy. For this step, we’re going to create a simple client in JavaScript and trace the request.
正如我们之前提到的,前向代理在应用程序层面上工作,所以自然地,根据客户端,我们有多种方式可以配置前向代理。对于这一步,我们将在JavaScript中创建一个简单的客户端,并跟踪请求。
Before we begin, let’s make sure that the latest node.js and npm are installed on our local machine. Next, we create the directory and the file for the client. Let’s call the directory Proxy Test and the file proxytest.js accordingly.
在我们开始之前,让我们确保最新的node.js和npm已经安装在我们的本地机器上。接下来,我们为客户端创建目录和文件。让我们将目录Proxy Test和文件proxytest.js相应地命名。
Next, we need to initialize NPM’s package.json so we can install all the needed libraries. We do this by running the npm init command on the terminal inside our project’s directory:
接下来,我们需要初始化NPM的package.json,以便我们能够安装所有需要的库。我们通过在项目目录下的终端上运行npm init命令来完成这一工作。
npm init
After we successfully initialize the repository, we need to install the request library that we’ll use for building the custom request with the proxy configuration:
在我们成功初始化资源库后,我们需要安装request库,我们将使用代理配置来构建自定义请求。
npm install request
Finally, let’s open an IDE and paste the below code into our proxytest.js file:
最后,让我们打开一个IDE,将下面的代码粘贴到我们的proxytest.js文件中。
var request = require('request');
request({
'url':'http://www.google.com/',
'method': "GET",
'proxy':'http://192.168.100.40:8888'
},function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
}
})
Now, let’s run this code snippet:
现在,让我们运行这个代码片段。
node proxytest.js
Let’s take a step back and look at each line. The first line imports the library into the request object, which we’ll use later on.
让我们退一步看一下每一行。第一行将库导入到request对象中,我们将在后面使用它。
Inside the request object, we specify the URL of the destination server, the HTTP Method, and the proxy as URL and port keypair. Inside the callback function, if the request is successful, we log the response body in the console.
在request对象中,我们指定目标服务器的URL,HTTP方法,以及作为URL和端口密钥对的代理。在回调函数中,如果请求成功,我们在控制台中记录响应体。
Next, let’s have a look at Nginx’s debug logs :
接下来,让我们看一下Nginx的调试日志。
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script copy: "http://"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script var: "www.google.com"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script var: "/"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script var: ""
2022/02/20 13:46:13 [debug] 1790#1790: *1 http init upstream, client timer: 0
2022/02/20 13:46:13 [debug] 1790#1790: *1 epoll add event: fd:7 op:3 ev:80002005
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script copy: "Host"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script var: "www.google.com"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script copy: "Connection"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script copy: "close"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script copy: ""
2022/02/20 13:46:13 [debug] 1790#1790: *1 http script copy: ""
2022/02/20 13:46:13 [debug] 1790#1790: *1 http proxy header:
"GET / HTTP/1.0
Host: www.google.com
Connection: close
"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http cleanup add: 0000560CE3CF5E30
2022/02/20 13:46:13 [debug] 1790#1790: *1 http finalize request: -4, "/?" a:1, c:2
2022/02/20 13:46:13 [debug] 1790#1790: *1 http request count:2 blk:0
2022/02/20 13:46:13 [debug] 1790#1790: *1 http run request: "/?"
2022/02/20 13:46:13 [debug] 1790#1790: *1 http upstream check client, write event:1, "/"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http upstream resolve: "/?"
2022/02/20 13:46:14 [debug] 1790#1790: *1 name was resolved to 142.250.184.100
2022/02/20 13:46:14 [debug] 1790#1790: *1 name was resolved to 2a00:1450:4002:406::2004
2022/02/20 13:46:14 [debug] 1790#1790: *1 get rr peer, try: 2
2022/02/20 13:46:14 [debug] 1790#1790: *1 get rr peer, current: 0000560CE3CF5EB8 -1
2022/02/20 13:46:14 [debug] 1790#1790: *1 stream socket 12
2022/02/20 13:46:14 [debug] 1790#1790: *1 epoll add connection: fd:12 ev:80002005
2022/02/20 13:46:14 [debug] 1790#1790: *1 connect to 142.250.184.100:80, fd:12 #3
As we can see, our initial request goes through the proxy. Immediately after, the proxy server launches the new request, containing all the data from the initial request, to the destination resource. After that, it takes the response from the resource and returns it to our client:
正如我们所看到的,我们的初始请求通过了代理。紧接着,代理服务器启动新的请求,包含初始请求中的所有数据,到目标资源。之后,它从资源中获取响应并将其返回给我们的客户端。
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy status 200 "200 OK"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Date: Sun, 20 Feb 2022 12:46:15 GMT"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Expires: -1"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Cache-Control: private, max-age=0"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Content-Type: text/html; charset=ISO-8859-1"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info.""
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Server: gws"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "X-XSS-Protection: 0"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "X-Frame-Options: SAMEORIGIN"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Set-Cookie: 1P_JAR=2022-02-20-12; expires=Tue, 22-Mar-2022 12:46:15 GMT; path=/; domain=.google.com; Secure"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Set-Cookie: NID=511=IkyJTmMt6I2b3fHpGNUwdfCkv1q9cjzyeUaxC-cxMZWcbmSi4sVlRlwXJUTRA9ujqQnK2v6DNyhitL3zPRSf7RSIHDCv8aYcUD7jp3vX4sE7ZkiprAWmJo9FlnUJtV9H0IzOFyPck15Jfs0zb1VeOMOjKZk0BZ0XRQ3gNptMOl8; expires=Mon, 22-Aug-2022 12:46:15 GMT; path=/; domain=.google.com; HttpOnly"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Accept-Ranges: none"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header: "Vary: Accept-Encoding"
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy header done
2022/02/20 13:46:14 [debug] 1790#1790: *1 xslt filter header
2022/02/20 13:46:14 [debug] 1790#1790: *1 HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 20 Feb 2022 12:46:14 GMT
Content-Type: text/html; charset=ISO-8859-1
Transfer-Encoding: chunked
Connection: close
Expires: -1
Cache-Control: private, max-age=0
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2022-02-20-12; expires=Tue, 22-Mar-2022 12:46:15 GMT; path=/; domain=.google.com; Secure
Set-Cookie: NID=511=IkyJTmMt6I2b3fHpGNUwdfCkv1q9cjzyeUaxC-cxMZWcbmSi4sVlRlwXJUTRA9ujqQnK2v6DNyhitL3zPRSf7RSIHDCv8aYcUD7jp3vX4sE7ZkiprAWmJo9FlnUJtV9H0IzOFyPck15Jfs0zb1VeOMOjKZk0BZ0XRQ3gNptMOl8; expires=Mon, 22-Aug-2022 12:46:15 GMT; path=/; domain=.google.com; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
2022/02/20 13:46:14 [debug] 1790#1790: *1 write new buf t:1 f:0 0000560CE3CF7AD0, pos 0000560CE3CF7AD0, size: 760 file: 0, size: 0
2022/02/20 13:46:14 [debug] 1790#1790: *1 http write filter: l:0 f:0 s:760
2022/02/20 13:46:14 [debug] 1790#1790: *1 http cacheable: 0
2022/02/20 13:46:14 [debug] 1790#1790: *1 http proxy filter init s:200 h:0 c:0 l:-1
2022/02/20 13:46:14 [debug] 1790#1790: *1 http upstream process upstream
When the request is successfully sent to the destination, we see on the logs a response “200 OK” meaning the request was accepted and the response was returned successfully. From our logs we can also see all the HTTP headers that the response returned, listed line by line. Whatever HTTP headers the destination server returns are automatically added to the Proxy return object.
当请求被成功发送到目的地时,我们在日志上看到一个 “200 OK “的响应,这意味着请求被接受,响应被成功返回。从我们的日志中,我们还可以看到响应返回的所有HTTP头信息,并逐行列出。目的地服务器返回的任何HTTP头信息都会自动添加到代理返回对象中。
5. Conclusion
5.结论
In this tutorial, we’ve learned how to set up an easy and lightweight forward proxy using the Nginx server. We’ve learned an important difference between a forward proxy and a VPN. Finally, we’ve also learned how to connect a JavaScript-based client to our newly created forward proxy.
在本教程中,我们已经学会了如何使用Nginx服务器建立一个简单和轻量级的前向代理。我们学习了前向代理和VPN之间的一个重要区别。最后,我们还学习了如何将一个基于JavaScript的客户端连接到我们新创建的前向代理。
The full source code can be found over on GitHub.
完整的源代码可以在GitHub上找到over。