How to combine rewrites with different roots in location blocks - nginx

I'm struggling to understand how to control rewrites to PHP scripts when the location blocks that do the rewrites require different roots.
Here's the simplified example. My catch-all front controller must be in the web root, but the firewall.php script must not be.
The purpose of this is to provide gated access to download files not under the root.
server {
# Requests for media forced through firewall script outside web root
# /path/to/secure-area/firewall.php
#
location ^~ /downloads/ {
root /path/to/secure-area;
rewrite ^/.+$ /firewall.php last;
}
# Regular requests bootstrap front controller under web-root
# /path/to/web-root/index.php;
#
location / {
root /path/to/web-root;
index index.php
if ( -e $request_filename ) {
break;
}
rewrite ^.+$ /index.php last;
}
# Execute PHP via php-fpm
# rewrites hitting this are not mapping request_filename properly
#
location ~ \.php$ {
if ( !-f $request_filename ) {
return 404;
}
include /usr/local/nginx/conf/fastcgi_params;
fastcgi_pass unix:/tmp/php.sock;
fastcgi_index index.php;
}
}
The obvious solution is having a common root, but this is not possible in my situation. I cannot place the secure location under the web root and the web root must stay as-is.
It looks as if the root directive is only effective within the location block that defines it. The rewrites are individually working fine, but when the ~ \.php block is hit, the root is lost.
I'm obviously doing it wrong, so how should I achieve this?

Untested, but something like this ought to help you. It uses the http://wiki.nginx.org/XSendfile to serve the protected content from a different root. Also uses try_files which is a much better pattern for Front Controllers.
server {
# More here: http://wiki.nginx.org/XSendfile
#
# To serve /downloads/some.zip
# get php to set the http header:
#
# X-Accel-Redirect: /downloads/some.zip
#
# and then the file /path/to/secure-area/downloads/some.zip
# will be sent by nginx
location /downloads/ {
internal;
root /path/to/secure-area;
}
location / {
root /path/to/web-root;
index index.php
try_files $uri $uri/ /index.php;
}
# make sure you read http://wiki.nginx.org/Pitfalls
location ~* \.php$ {
try_files $uri =404;
fastcgi_pass unix:/tmp/php.sock;
fastcgi_index index.php;
include /usr/local/nginx/conf/fastcgi_params;
}
}

Related

Nginx Wordpress Internal rewrite WITHOUT changing URL/URI

So I'm trying to do something unique, I've spent a few hours reading about NGINX rewrite and how to create a CATCH ALL for a specific page.
What I want to do:
mysite.com/Subdivision/ is a wordpress PAGE.
I want to be able to randomly generate (TAIL PAGES) that all by default go to the Higher up wordpress page (mysite.com/Subdivision/) :
mysite.com/Subdivision/Green-Trails
mysite.com/Subdivision/River-Oaks
mysite.com/Subdivision/Creek-Plantation
And then inside of my /Subdivision/ page, I will write script telling it what to do with "Green-Trails" and "River-Oaks", and "Creek-Plantation"
After that works, the goal is to also add other stuff like
mysite.com/Subdivision/Green-Trails/4-bdr/with-pool/
and my /Subdivision page will have settings "IF 4-bdr" is found in the Request-URI, set this. IF with-pool is found in the Request URI, Set this... This will all be done in PHP snippet codes.
Just have to get past the NGINX write hurdle.
This is my current Centmin Setup with NGINX:
## BEGIN WORDPRESS MOD ##
index index.php index.html index.htm;
# enforce www (exclude certain subdomains)
if ($host !~* ^(www|subdomain))
{
# rewrite ^/(.*)$ $scheme://www.$host/$1 permanent;
}
# enforce NO www if ($host ~* ^www\.(.*)) {
# set $host_without_www $1; rewrite ^/(.*)$ $scheme://$host_without_www/$1
permanent;
#}
# unless the request is for a valid file, send to bootstrap
if (!-e $request_filename)
{
rewrite ^(.+)$ /index.php?q=$1 last;
}
location ~ /subdivision/(.*) {
index index.php;
try_files $uri /index.php?q=/subdivision/&$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass localhost:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
## END WORDPRESS MOD ##
As you can see:
location ~ /subdivision/(.*) {
index index.php;
try_files $uri /index.php?q=/subdivision/&$args;
}
Is where I am trying to tell NGINX to SERVE the /Subdivision/ page variable to wordpress and tell Wordpress to IGNORE the rest of the URL /Green-Trails/ /River-Oaks/, /Creek-Plantation/
But this isn't working, can someone please help me, where did I miss up? I do NOT want to change the URI in the browser, this is key. Is this possible?

Nginx separate apps in subdirectories

I am trying to move an app from apache server to nginx. The problem is that there are multiple apps in subdirectories and I can't find a proper way to configure the server.
What I need:
www.example.com serves from /srv/app
www.example.com/sub1 serves from /srv/app/sub1
www.example.com/sub2 serves from /srv/app/sub2
Each of the apps need the same config, so I extracted that in a snippet:
# snippets/app.conf
index index.php index.html index.htm index.nginx-debian.html;
location /system {
return 403;
}
# [a couple of other 403s excluded]
# Pass non-file URI to index.php
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Use PHP
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
# Hide .htaccess
location ~ /\.ht {
deny all;
}
And in the main server file:
# [non-www and http redirects]
server {
# [listen directives]
server_name www.example.com;
root /srv/app;
include snippets/app.conf;
location /sub1 {
root /srv/app/sub1;
include snippets/app.conf;
}
# [other sub-apps included in the same way]
# [ssl stuff]
}
However, this gives me an error:
nginx: [emerg] location "/system" is outside location "/sub1" in /etc/nginx/snippets/app.conf:5
It's obvious from the error that /system is interpreted as being "absolute" www.example.com/system instead of the nested www.example.com/sub1/system. Can I somehow specify that I want the nested locations to be considered relative? Or I just have to repeat the whole near-identical config for every sub-app changing the prefixes?
It turns out that most of the repeating is not necessary in nginx.
The directives to use fastcgi for .php and hide /.ht files were already regexes so they affect everything. It's enough to specify index once and the default there stuff was redundant if I only want to use index.php.
As all the apps are nested on the filesystem in the same way as on web, specifying root was not necessary either.
What surprised me was that location ^~ /(system|data)/ { ... } matches not only www.example.com/system/, but also www.example.com/sub1/system/. I thought that ^~ should match only if the location start matches the regex...
# [non-www and http redirects]
server {
# [listen directives]
server_name www.example.com;
root /srv/app;
index index.php;
location ^~ /(system|data)/ {
return 403;
}
# Use PHP
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
# Pass non-file URI to index.php for all locations
location /sub1/ {
try_files $uri $uri/ /sub1/index.php?$query_string;
}
location /sub2/ {
try_files $uri $uri/ /sub2/index.php?$query_string;
}
# [other sub-apps included in the same way]
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ /\.ht {
deny all;
}
# [ssl stuff]
}
I also tried to replace the separate locations with
location ^~ /(sub1|sub2)/ {
try_files $uri $uri/ /$1/index.php?$query_string;
}`
but didn't succeed with that - this location somehow didn't ever match and everything got passed to the /index.php in base instead.

Nginx configuration for rewrite rule

So I have this configuration at the moment in nginx:
autoindex off;
location / {
try_files $uri $uri.html $uri/ #extensionless-php;
index index.html index.htm index.php;
}
location #extensionless-php {
rewrite ^(.*)$ $1.php last;
}
# pass PHP scripts to FastCGI server
#
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# With php-fpm (or other unix sockets):
fastcgi_pass unix:/var/run/php/php7.1-fpm.sock;
# With php-cgi (or other tcp sockets):
#fastcgi_pass 127.0.0.1:9000;
}
Now, I want to get something 'special'. I want to get an url (http://project2.local/camera?camera_id=1) to be rewritten as http://project2.local/camera/1 I've tried this code;
location / {
rewrite ^/?camera\.php$ /camera/%1? redirect;
}
location /camera {
rewrite ^/camera/([^/]*)$ /camera.php?camera_id=$1 break;
}
but that downloads something empty when I navigate to that place.. What am I doing wrong here?
Try:
location /camera {
rewrite ^/camera/([^/]*)$ /camera.php?camera_id=$1 last;
}
The rewrite...break will prevent the camera.php URI being processed by the PHP location. You need to use last as per your existing configuration. See this document for details.
You also added: location / { rewrite ^/?camera\.php$ /camera/%1? redirect; }, but it seems wrong and superfluous for a number of reasons:
You cannot have two location / blocks (perhaps you meant you added the line of code to the existing location / block)
I do not recognise the %1 term (the value of the parameter would be $arg_camera_id)
It will never match anything (URIs ending with .php are processed by the PHP location)

nginx rewrite & parameter full-url and file extensions

I will try to be brief. I have the following nginx rewrite url:
rewrite ^/(.*)?$ /index.php?completeURL=$1 last;
I want a url like:
http://mywebsite.com/http://www.otherwebsite.com/dir1/dirx/article.php&id=2&category=1
request:
http://mywebsite.com/index.php?completeURL=http://www.otherwebsite.com/dir1/dirx/article.php&id=2&category=1
Currently the nginx rule have a problem. Example: If the parameter contains a .php extension he looks for that file on my server.
Example: http://mywebsite.com/dir1/dirx/article.php
How can I solve this problem in your opinion?
UPDATE:
here the nginx configuration (and rewrite) files:
(config) https://gist.github.com/ivanionut/cc53c9de372b932c3937d9394d3b448c
(rewrite) https://gist.github.com/ivanionut/4df3ad9b858a54ae01461ab078adffb6
The simplest solution (assuming that the server does nothing else other than serve index.php) is to remove the usual location ~ \.php$ block and perform a rewrite ... break; in the same block as the fastcgi_pass. There are a number of ways of achieving this, including:
location / {
rewrite ^/(.*)?$ /index.php?completeURL=$1 break;
fastcgi_pass ...
...
}
An alternative strategy is to perform the rewrite only if a local file does not already exist, but you need to ensure that .php files are tested too. For example:
location / {
try_files $uri $uri/ #rewrite;
}
location ~ \.php$ {
try_files $uri #rewrite;
fastcgi_pass ...
...
}
location #rewrite {
rewrite ^/(.*)?$ /index.php?completeURL=$1 last;
}

Nginx location behave with and without location regex

To include my small project (not based on one of the known frameworks) into existing website, I've added the following config to Nginx
server {
listen 80 default_server;
server_name localhost;
access_log /var/log/nginx/dev.access.log;
error_log /var/log/nginx/dev.error.log;
root /var/www;
index index.php;
[...]
location /www.my-project.com {
alias /var/www/www.my-project.com/web;
index index.php;
if (-f $request_filename) { break; }
rewrite ^(.*)$ /www.my-project.com/index.php last;
location ~ /[^/]+/index\.php$ {
include fastcgi_params;
fastcgi_pass unix:/var/run/fcgi.sock;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
}
}
All works fine (except that I wish to prevent to list subdir name in location directive), so I can call http://localhost/www.my-project.com. But when calling http://localhost/www.my-project.com.blabla the location directive from above is called and my internal error page is served. So I tried to change location directive to
location ~ ^/www\.my-project\.com(/|$) {
But that causes any existing file (CSS, JS...) to be rewritten to index.php, which then returns an 404 itself. Why does a change of location causes this horrible behaviour, I can see no logical difference between location /www.my-project.com and location ~ ^/www\.my-project\.com(/|$).
I'd suggest excluding the assets from the rewrite, you can do that by adding a new location, something like this
location /(css|js|images) {
root /var/www/www.my-project.com/web;
try_files $uri =404;
}
And for the location issue, you can match exact locations using =
location = /www.my-project.com {

Resources