是否有与 Apaches 的 {ENV:REDIRECT_STATUS} 等效的 Nginx 环境变量?
Is there a Nginx environment variable equivalent to Apaches' {ENV:REDIRECT_STATUS}?
从 Apache 迁移到 Nginx 时,.htaccess 文件中的某些规则必须 'translated' 到 Nginx 配置文件。
一个我似乎无法解决的问题,一个例子是最简单的解释方法:
请求 http://www.domain.com/nginx
被 Apache 内部重写为 index.php?option=com_content&view=article&id=145
现在我想阻止对 index.php?option=com_content
的直接请求,因此该页面只能通过 http://www.domain.com/nginx
访问以避免重复内容。在 Apache 中,这是通过使用这些 .htaccess 规则实现的:
# Check if it's the first pass to prevent a loop. In case of first pass, the environment variable contains nothing
# If http://www.domain.com/nginx already internally has been rewritten to index.php?option=com_content&view=article&id=145 {ENV:REDIRECT_STATUS} contains '200' and the request is allowed to be processed
RewriteCond %{ENV:REDIRECT_STATUS} ^$
# Check if the query string contains requests for the page
RewriteCond %{QUERY_STRING} ^index.php?option=com_content&view=article&id=145 [NC]
# If conditions apply, reject request
RewriteRule .* 404 [L]
在Nginx中,有这样的环境变量可以使用吗?
或者我应该以完全不同的方式处理这个问题?
编辑 1:
在现实生活中,它不仅是一个页面,而且是一个包含很多页面的动态 Joomla 站点。我测试了上面的方法,但目的是阻止 index.php?option_content&view=article&id=*
上的所有请求
编辑 2:
这是工作的 NGINX 配置文件:
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html/domainname;
index index.php index.html index.htm;
server_name domainname.com;
server_name localhost;
location / {
try_files $uri $uri/ /index.php?$args;
}
# deny running scripts inside writable directories
location ~* /(images|cache|media|logs|tmp)/.*\.(php|pl|py|jsp|asp|sh|cgi)$ {
return 403;
error_page 403 /403_error.html;
}
## give 404 header & redirect to custom errorpage without changing URL ##
error_page 404 = /404_custom.php; #global error page, script handles header
error_page 500 502 503 504 /50x.html;
location =/index.php {
set $arg_set "${arg_option}___${arg_view}___${arg_id}";
if ($arg_set ~* "^(((\w|-)+?)___){2}((\w|-)+?)$") {
return 404;
}
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
}
Apache 方法在这里不起作用,但您可以通过许多其他方法解决此问题,具体取决于您要实施的此类规则数量和其他一些条件。在一般情况下,我会使用这样的东西:
map "${arg_option}___${arg_view}___${arg_id}" $show404 {
default 0;
# Put here the argument value sets of the pages
# you want to hide - one set per line
"com_content___article___145" 1;
}
server {
...
location /nginx {
rewrite ^.*$ /index.php?option=com_content&view=article&id=145 break;
proxy_pass ...
}
location =/index.php {
if ($show404) {
return 404;
}
proxy_pass ...;
}
...
}
编辑:
如果您想阻止对 index.php 的所有请求,其中存在参数 "option"、"view" 和 "id",无论它们的值是什么,您可以使用这样的东西:
location =/index.php {
set $arg_set "${arg_option}___${arg_view}___${arg_id}";
if ($arg_set ~* "^(((\w|-)+?)___){2}((\w|-)+?)$") {
return 404;
}
proxy_pass ...
}
如果需要检查这些参数的某些值,只需修改正则表达式以适合您的目的:
location =/index.php {
set $arg_set "${arg_option}___${arg_view}___${arg_id}";
if ($arg_set ~* "^com_content___article___(\d+)$") {
return 404;
}
proxy_pass ...
}
此外,在您的情况下,可以使用 map 来简化配置,这样您就不必为每篇文章添加另一个位置,而是将所有重写规则封装在一个 map 块中,如下所示:
map "$request_uri" $real_args {
default "";
"~*^/nginx" option=com_content&view=article&id=145;
"~*^/some_article" option=com_content&view=news&id=123;
"~*^/another_article" option=com_content&view=article&id=515;
}
server {
...
location / {
if ($real_args) {
rewrite ^.*$ /index.php?$real_args break;
}
proxy_pass ...
}
location =/index.php {
# See above
}
...
}
编辑 2:
对于一两个异常,您可以使用 negative look-ahead:
改进正则表达式
if ($arg_set ~* "^(((\w|-)+?)___){2}((?!175$)(\w|-)+?)$") {
return 404;
}
但是如果您希望有很多这样的 URL,那么您最终必须在您的配置中引入地图。否则你的正则表达式会变得太复杂和难以管理。本例中的配置如下所示:
map "${arg_option}___${arg_view}___${arg_id}" $exception {
default 0;
"com_content___article___175" 1;
"com_content___news___188" 1;
"something___else___211" 1;
}
server {
...
location =/index.php {
set $arg_set "${arg_option}___${arg_view}___${arg_id}";
if ($exception) {
break;
}
if ($arg_set ~* "^(((\w|-)+?)___){2}((\w|-)+?)$") {
return 404;
}
proxy_pass ...;
}
...
}
这似乎有点违反直觉,但这正是 "if" 在 Nginx 中的工作方式。如果 Nginx 在第一个 "if" 块中遇到中断,则不会评估第二个 "if"。
从 Apache 迁移到 Nginx 时,.htaccess 文件中的某些规则必须 'translated' 到 Nginx 配置文件。 一个我似乎无法解决的问题,一个例子是最简单的解释方法:
请求 http://www.domain.com/nginx
被 Apache 内部重写为 index.php?option=com_content&view=article&id=145
现在我想阻止对 index.php?option=com_content
的直接请求,因此该页面只能通过 http://www.domain.com/nginx
访问以避免重复内容。在 Apache 中,这是通过使用这些 .htaccess 规则实现的:
# Check if it's the first pass to prevent a loop. In case of first pass, the environment variable contains nothing
# If http://www.domain.com/nginx already internally has been rewritten to index.php?option=com_content&view=article&id=145 {ENV:REDIRECT_STATUS} contains '200' and the request is allowed to be processed
RewriteCond %{ENV:REDIRECT_STATUS} ^$
# Check if the query string contains requests for the page
RewriteCond %{QUERY_STRING} ^index.php?option=com_content&view=article&id=145 [NC]
# If conditions apply, reject request
RewriteRule .* 404 [L]
在Nginx中,有这样的环境变量可以使用吗? 或者我应该以完全不同的方式处理这个问题?
编辑 1: 在现实生活中,它不仅是一个页面,而且是一个包含很多页面的动态 Joomla 站点。我测试了上面的方法,但目的是阻止 index.php?option_content&view=article&id=*
上的所有请求编辑 2: 这是工作的 NGINX 配置文件:
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
root /usr/share/nginx/html/domainname;
index index.php index.html index.htm;
server_name domainname.com;
server_name localhost;
location / {
try_files $uri $uri/ /index.php?$args;
}
# deny running scripts inside writable directories
location ~* /(images|cache|media|logs|tmp)/.*\.(php|pl|py|jsp|asp|sh|cgi)$ {
return 403;
error_page 403 /403_error.html;
}
## give 404 header & redirect to custom errorpage without changing URL ##
error_page 404 = /404_custom.php; #global error page, script handles header
error_page 500 502 503 504 /50x.html;
location =/index.php {
set $arg_set "${arg_option}___${arg_view}___${arg_id}";
if ($arg_set ~* "^(((\w|-)+?)___){2}((\w|-)+?)$") {
return 404;
}
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
}
Apache 方法在这里不起作用,但您可以通过许多其他方法解决此问题,具体取决于您要实施的此类规则数量和其他一些条件。在一般情况下,我会使用这样的东西:
map "${arg_option}___${arg_view}___${arg_id}" $show404 {
default 0;
# Put here the argument value sets of the pages
# you want to hide - one set per line
"com_content___article___145" 1;
}
server {
...
location /nginx {
rewrite ^.*$ /index.php?option=com_content&view=article&id=145 break;
proxy_pass ...
}
location =/index.php {
if ($show404) {
return 404;
}
proxy_pass ...;
}
...
}
编辑:
如果您想阻止对 index.php 的所有请求,其中存在参数 "option"、"view" 和 "id",无论它们的值是什么,您可以使用这样的东西:
location =/index.php {
set $arg_set "${arg_option}___${arg_view}___${arg_id}";
if ($arg_set ~* "^(((\w|-)+?)___){2}((\w|-)+?)$") {
return 404;
}
proxy_pass ...
}
如果需要检查这些参数的某些值,只需修改正则表达式以适合您的目的:
location =/index.php {
set $arg_set "${arg_option}___${arg_view}___${arg_id}";
if ($arg_set ~* "^com_content___article___(\d+)$") {
return 404;
}
proxy_pass ...
}
此外,在您的情况下,可以使用 map 来简化配置,这样您就不必为每篇文章添加另一个位置,而是将所有重写规则封装在一个 map 块中,如下所示:
map "$request_uri" $real_args {
default "";
"~*^/nginx" option=com_content&view=article&id=145;
"~*^/some_article" option=com_content&view=news&id=123;
"~*^/another_article" option=com_content&view=article&id=515;
}
server {
...
location / {
if ($real_args) {
rewrite ^.*$ /index.php?$real_args break;
}
proxy_pass ...
}
location =/index.php {
# See above
}
...
}
编辑 2:
对于一两个异常,您可以使用 negative look-ahead:
改进正则表达式if ($arg_set ~* "^(((\w|-)+?)___){2}((?!175$)(\w|-)+?)$") {
return 404;
}
但是如果您希望有很多这样的 URL,那么您最终必须在您的配置中引入地图。否则你的正则表达式会变得太复杂和难以管理。本例中的配置如下所示:
map "${arg_option}___${arg_view}___${arg_id}" $exception {
default 0;
"com_content___article___175" 1;
"com_content___news___188" 1;
"something___else___211" 1;
}
server {
...
location =/index.php {
set $arg_set "${arg_option}___${arg_view}___${arg_id}";
if ($exception) {
break;
}
if ($arg_set ~* "^(((\w|-)+?)___){2}((\w|-)+?)$") {
return 404;
}
proxy_pass ...;
}
...
}
这似乎有点违反直觉,但这正是 "if" 在 Nginx 中的工作方式。如果 Nginx 在第一个 "if" 块中遇到中断,则不会评估第二个 "if"。