英文:
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)
<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>
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 ^(.*)$ "/$1" [S=1]
RewriteRule ^ - [P]
ProxyPass / http://localhost:8080/
答案1
得分: 1
在Apache配置中,要正确处理PHP脚本,您需要确保在决定是否代理请求之前,Apache会执行PHP。您可以使用mod_proxy_fcgi
与php-fpm
来实现这一点。以下是如何调整配置的方法:
假设已经安装和配置了php-fpm
,然后您需要配置Apache与之一起使用(需要必要的模块:mod_rewrite
,mod_proxy
,mod_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地址和端口。 -
RewriteCond
和RewriteRule
指令类似于前面的示例,如果它们存在,则提供文件和目录。 -
请求被代理到后端服务器,当文件不存在时。
-
删除了
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:
<?php
$backend_url = 'http://localhost:8080' . $_SERVER['REQUEST_URI'];
$response = file_get_contents($backend_url);
if ($response === false) {
// The backend server returned a 404 error
http_response_code(404);
echo "Not found";
} 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]
:
<VirtualHost *:80>
ServerName your-domain.com
DocumentRoot /path/to/your/local/files
# Handle PHP scripts with php-fpm
<FilesMatch \.php$>
SetHandler "proxy:unix:/path/to/your/php-fpm.sock|fcgi://localhost"
</FilesMatch>
# 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
</VirtualHost>
That includes:
-
the
FilesMatch
directive tells Apache to usephp-fpm
for handling PHP scripts. Make sure to set the correct path to yourphp-fpm
socket or TCP address and port if using TCP instead of a Unix socket. -
the
RewriteCond
andRewriteRule
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 ofRewriteRule
with the[P]
flag instead. This flag tellsmod_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 localErrorDocument
instead. -
the
RewriteCond
andRewriteRule
for directories will internally redirect to theindex.php
file if the request URI corresponds to a directory. This essentially mimics theDirectoryIndex
directive.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论