Apache2配置仅反向代理本地404的资源。

huangapple go评论63阅读模式
英文:

Apache2 configuration that ONLY reverse proxies resources that 404 locally

问题

我试图创建一个适用于Apache2的工作配置,实现以下功能:

  • 解析本地资源,要么返回静态文件,要么运行一些 PHP。
  • 如果要返回 404 错误,则检查反向代理。
  • 如果反向代理返回 404 错误,则返回本地的 ErrorDocument。

我想要实现类似以下的配置:

ProxyPassMatch ^/(?!200).+
ProxyPassReverse / http://localhost:8080/ nocanon
ProxyErrorOverride On
ProxyPreserveHost On

问题在于 ProxyPassMatch 只在 URL 上起作用,而不是状态码。在 nginx 中,我可以使用以下配置来实现所需的功能:

location / {
    try_files $uri $uri/ /index.php?$args @fwd;
}

location @fwd{
    proxy_pass http://localhost:8080;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_intercept_errors on;
    recursive_error_pages on;
}

有人有任何想法如何在 Apache2 中实现我的 nginx 配置吗?

编辑:

我觉得类似以下的配置应该可以工作(我从这个答案中获取了一些灵感:https://stackoverflow.com/questions/1582673/using-mod-rewrite-only-if-a-404-would-have-occured):

<Location /[a-z]+/>
    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} -s [OR]
    RewriteCond %{REQUEST_FILENAME} -l [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^.*$ - [NC,L]
    ProxyPassReverse http://localhost:8080
</Location>

我也尝试过以下配置:

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

RewriteRule ^ - [P]
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/

以及

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ - [L,P]
ProxyPass / http://localhost:8080/

这似乎在静态文件上有效,但不适用于可能被渲染的任何 PHP,不确定原因。

RewriteCond /var/www/html_apache/%{REQUEST_FILENAME} -f
RewriteRule ^(.*)$ "/$1" [S=1]
RewriteRule ^ - [P]
ProxyPass / http://localhost:8080/
英文:

I'm trying and failing to create a working configuration for apache2 which does the following:

  • Resolves resources local either by returning the static file or running some PHP
  • If a 404 is to be returned checks against the reverse proxy
  • If the reverse proxy returns a 404 return the local ErrorDocument

I want to do something like

ProxyPassMatch ^/(?!200).+
ProxyPassReverse / http://localhost:8080/ nocanon
ProxyErrorOverride On
ProxyPreserveHost On

The problem here is that ProxyPassMatch only works on the URL not the status code. Now in nginx I can use this configuration to do what I want.

location / {
    try_files $uri $uri/ /index.php?$args @fwd;
}

location @fwd{
    proxy_pass http://localhost:8080;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_intercept_errors on;
    recursive_error_pages on;
}

Does anyone have any ideas how I can implement my nginx configuration in apache2?

EDIT

I feel like something like this should work (which I got by taking a look at this answer https://stackoverflow.com/questions/1582673/using-mod-rewrite-only-if-a-404-would-have-occured)

&lt;Location /[a-z]+/&gt;
    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} -s [OR]
    RewriteCond %{REQUEST_FILENAME} -l [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^.*$ - [NC,L]
    ProxyPassReverse http://localhost:8080
&lt;/Location&gt;

I've also tried

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d                                                                                                                                                                                      RewriteRule ^ - [L]

RewriteRule ^ - [P]
ProxyPass / http://localhost:8080/
ProxyPassReverse / http://localhost:8080/

and

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ - [L,P]
ProxyPass / http://localhost:8080/

This seems to work but only on static files. It doesn't work on any PHP that might be rendered, not sure why

RewriteCond /var/www/html_apache/%{REQUEST_FILENAME} -f
RewriteRule ^(.*)$ &quot;/$1&quot; [S=1]
RewriteRule ^ - [P]
ProxyPass / http://localhost:8080/

答案1

得分: 1

在Apache配置中,要正确处理PHP脚本,您需要确保在决定是否代理请求之前,Apache会执行PHP。您可以使用mod_proxy_fcgiphp-fpm来实现这一点。以下是如何调整配置的方法:

假设已经安装和配置了php-fpm,然后您需要配置Apache与之一起使用(需要必要的模块:mod_rewritemod_proxymod_proxy_fcgi),这意味着您需要告诉Apache将PHP脚本传递给php-fpm执行。您可以使用ProxyPassMatch指令来实现这一目的。
保留mod_rewrite规则以提供本地资源或代理到上游,同时确保通过php-fpm处理PHP请求。

所有请求都经过反向代理。

这可能是由于ProxyPass指令的放置位置。ProxyPass指令在mod_rewrite规则之前执行。这意味着代理将用于所有请求,绕过mod_rewrite规则。

相反,让我们使用带有[P]RewriteRule指令:

<VirtualHost *:80>
    ServerName your-domain.com
    DocumentRoot /path/to/your/local/files

    # 使用php-fpm处理PHP脚本
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/path/to/your/php-fpm.sock|fcgi://localhost"
    </FilesMatch>

    # 启用重写引擎
    RewriteEngine On

    # 如果请求是目录,请内部重定向到DirectoryIndex
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
    RewriteRule ^(.*/)$ $1index.php [L]

    # 如果请求对应于真实存在的文件,请直接提供
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
    RewriteRule ^ - [L]

    # 如果请求不是真实文件,请将其代理到后端
    RewriteRule ^/(.*)$ http://localhost:8080/$1 [P,L]

    ProxyPassReverse / http://localhost:8080/
    ProxyPreserveHost On
    ProxyErrorOverride On

    # 404错误处理的错误文档
    ErrorDocument 404 /path/to/your/error/document.html
</VirtualHost>

这包括:

  • FilesMatch指令告诉Apache使用php-fpm处理PHP脚本。确保设置正确的路径到您的php-fpm套接字,或者如果使用TCP而不是Unix套接字,则设置正确的TCP地址和端口。

  • RewriteCondRewriteRule指令类似于前面的示例,如果它们存在,则提供文件和目录。

  • 请求被代理到后端服务器,当文件不存在时。

  • 删除了ProxyPass指令,而是使用带有[P]标志的RewriteRule代替。这个标志告诉mod_rewrite代理请求,有效地替换了ProxyPass指令,同时确保遵守重写条件。

  • 当后端服务器返回404时,使用本地的ErrorDocument

  • ProxyErrorOverride On告诉Apache处理代理目标的错误响应(如404),并使用本地的ErrorDocument

英文:

While "RewriteCond file exists" is not an exact duplicate, it includes an important reminder:

> Remember that RewriteCond with -f or -d needs full path to the file or directory that's why you need to prefix the filename with %{DOCUMENT_ROOT}/.

For example, this configuration first checks if the requested URI corresponds to a real file or directory in the document root.

  • If it does, the request is served directly and the rewriting process stops ([L] flag).
  • If the requested URI does not correspond to a real file or directory, the request is proxied to the backend server ([P] flag).
RewriteEngine On

# If the request is for a real existing file or directory, serve it directly
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

# If the request is not for a real file or directory, proxy it to the backend
RewriteRule ^/(.*)$ http://localhost:8080/$1 [P,L]

# Set up reverse proxy
ProxyPassReverse / http://localhost:8080/

Warning: this would not handle PHP execution. If you want Apache to execute PHP scripts before deciding whether to proxy the request, you will need to make sure you have php-fpm properly configured, in which case you might need to adjust the RewriteCond directives to also consider URIs that correspond to PHP scripts as "real" files.

You can also consider using Apache's ErrorDocument directive in combination with mod_rewrite to handle 404 errors. This approach could potentially be used to implement a fallback to a reverse proxy when a local resource is not found.

# Enable rewrite engine
RewriteEngine On

# If the request is for a real existing file or directory, serve it directly
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]

# If the request is not for a real file or directory, return a 404 error
# The ErrorDocument directive will handle this
RewriteRule ^ - [R=404]

# Set up a custom 404 error handler
ErrorDocument 404 /handle404.php

In this configuration, /handle404.php is a PHP script that proxies the request to the backend server.
Here is a very basic example of what that script might look like:

&lt;?php
$backend_url = &#39;http://localhost:8080&#39; . $_SERVER[&#39;REQUEST_URI&#39;];
$response = file_get_contents($backend_url);

if ($response === false) {
    // The backend server returned a 404 error
    http_response_code(404);
    echo &quot;Not found&quot;;
} else {
    // The backend server returned a successful response
    echo $response;
}

This script uses PHP's file_get_contents function to send a request to the backend server and capture its response.
If the backend server returns a 404 error, the script sends a 404 response to the client. Otherwise, it echoes the backend server's response.


> How exactly would you go about handling the php case?

To handle PHP scripts properly in the Apache configuration, you need to ensure that PHP is executed by Apache before deciding whether to proxy the request. You can achieve this by using mod_proxy_fcgi along with php-fpm. Here is how you can adjust the configuration:

Assuming php-fpm is installed and configured, you then need to configure Apache to it (with necessary modules: mod_rewrite, mod_proxy, mod_proxy_fcgi), meaning you need to tell Apache to pass PHP scripts to php-fpm for execution. You can use the ProxyPassMatch directive for this purpose.
While you keep the mod_rewrite rules to serve local resources or proxy to a backend shown above, you make sure PHP requests are handled by php-fpm.

> All requests go through to the reverse proxy.

This can be due to the placement of the ProxyPass directive. The ProxyPass directive is executed before the mod_rewrite rules. That means the proxy will be used for all requests, bypassing the mod_rewrite rules.

Instead, let's use a RewriteRule directive with [P]:

&lt;VirtualHost *:80&gt;
    ServerName your-domain.com
    DocumentRoot /path/to/your/local/files

    # Handle PHP scripts with php-fpm
    &lt;FilesMatch \.php$&gt;
        SetHandler &quot;proxy:unix:/path/to/your/php-fpm.sock|fcgi://localhost&quot;
    &lt;/FilesMatch&gt;

    # Enable rewrite engine
    RewriteEngine On

    # If the request is for a directory, internally redirect to the DirectoryIndex
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
    RewriteRule ^(.*/)$ $1index.php [L]

    # If the request is for a real existing file, serve it directly
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f
    RewriteRule ^ - [L]

    # If the request is not for a real file, proxy it to the backend
    RewriteRule ^/(.*)$ http://localhost:8080/$1 [P,L]

    ProxyPassReverse / http://localhost:8080/
    ProxyPreserveHost On
    ProxyErrorOverride On

    # Error document for handling 404
    ErrorDocument 404 /path/to/your/error/document.html
&lt;/VirtualHost&gt;

That includes:

  • the FilesMatch directive tells Apache to use php-fpm for handling PHP scripts. Make sure to set the correct path to your php-fpm socket or TCP address and port if using TCP instead of a Unix socket.

  • the RewriteCond and RewriteRule directives are similar to the previous example and serve files and directories if they exist.

  • the request proxied to the backend server, when the file does not exist.

  • the removal ProxyPass directive, in favor of RewriteRule with the [P] flag instead. This flag tells mod_rewrite to proxy the request, effectively replacing the ProxyPass directive, but also ensuring that the rewrite conditions are respected.

  • the local ErrorDocument, when the backend server returns a 404.

  • ProxyErrorOverride On tells Apache to handle error responses (like 404) from the proxy target and use the local ErrorDocument instead.

  • the RewriteCond and RewriteRule for directories will internally redirect to the index.php file if the request URI corresponds to a directory. This essentially mimics the DirectoryIndex directive.

huangapple
  • 本文由 发表于 2023年6月19日 18:50:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76505911.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定