Trouble adding /pma location to all virtual hosts

Hello,

I’m trying to accomplish something that feels like it should be very
simple, yet I’m struggling. I’m new to nginx, and I feel a bit lost as I
try to “translate” everything that I’ve done in Apache over the years to
nginx. So, please bear with me. I’ve done my research and asking this
list for help is a last-resort.

I have an application, phpMyAdmin, installed in /var/www/pma. I would
like to modify the nginx configuration such that every virtual-host
whose configuration file is located in /etc/nginx/sites-available/ has
access to the files in this directory by browsing to the location /pma/,
relative to the domain root.

The filesystem information for /var/www/pma is as follows (the
permissions are set recursively on the entire directory – for now):

ls -lah /var/www | grep “pma”

drwxrwxr-x 9 www-data www-data 4.0K Jun 17 16:37 pma

I figured that it might be simpler to get phpMyAdmin working for a
single vhost before attempting the same move server-wide.

On the surface, it looks to be this simple:

location /pma/ {
alias /var/www/pma/;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
}

When I try this configuration, I have the following in error.log:

2013/06/25 14:04:07 [error] 29741#0: *21 FastCGI sent in stderr:
“Primary script unknown” while reading response header from upstream,
client: 1.2.3.4, server: example.com, request: “GET /pma/ HTTP/1.1”,
upstream: “fastcgi://unix:/var/run/php5-fpm.sock:”, host: “example.com

While researching the cause of this error, I have seen others state that
SCRIPT_FILENAME has to be modified when using an alias in this way, e.g.

fastcgi_param SCRIPT_FILENAME $request_filename;

but the error messages are the same with this line, too.

So, I tried to use the “root” directive, instead of “alias”, as I have
no particular reason for using one over the other in this scenario.

location /pma/ {
##alias /var/www/pma/;
root /var/www;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
}

This “kind of works”. The index file at location /pma/index.php is
parsed via PHP, but requests for all other resources on the page yield
“403 Forbidden”. The log states:

2013/06/25 14:21:46 [error] 30343#0: *12 FastCGI sent in stderr: “Access
to the script ‘/var/www/pma/favicon.ico’ has been denied (see
security.limit_extensions)” while reading response header from upstream,
client: 1.2.3.4, server: example.com, request: “GET /pma/favicon.ico
HTTP/1.1”, upstream: “fastcgi://unix:/var/run/php5-fpm.sock:”, host:
example.com

Obviously, the aim here is not to execute ‘/var/www/pma/favicon.ico’ as
a PHP script.

I found a thread at

which seems to address this intended behavior (the rationale is sound).
So, I split my configuration up into the following sections, so that PHP
scripts would be handled via php-fpm and static content would be handled
directly:

location ~ /pma/.*.php$ {
root /var/www;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
}

location /pma/ {
root /var/www;
# Adding the following line makes no difference:
index index.php;
}

With this configuration, PMA’s index page won’t even load. The location
/pma/ returns a 404, as does /pma/index.php.

Nothing is written to the vhost’s error.log when /pma/ or /pma/index.php
is requested. Only the following (I’ve omitted the irrelevant bits) is
written to access.log:

“GET /pma/ HTTP/1.1” 404 200 “-”
“GET /pma/index.php HTTP/1.1” 404 200 “-”

I must be doing something completely asinine.

Other misc. details:

  • PHP’s open_basedir directive includes the path /var/www/pma.

  • nginx is executing the request as the user “web2” who is in the group
    “client2” (this is configured via ISPConfig).

  • The group “client2” is in the group “www-data”, and /var/www/pma’s
    user:group is www-data:www-data and the permissions on the directory are
    0775, recursively.

Thanks in advance for any help here,

-Ben

I was able to accomplish my objective with some help from the tutorial
at:
http://www.howtoforge.com/running-phpmyadmin-on-nginx-lemp-on-debian-squeeze-ubuntu-11.04

location /pma {
root /var/www/;
index index.php index.html index.htm;
location ~ ^/pma/(.+.php)$ {
try_files $uri =404;
root /var/www/;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_param HTTPS on;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
location ~* ^/pma/(.+.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
root /var/www/;
}
}
location /PMA {
rewrite ^/* /pma last;
}

I don’t know if the key is the nested location block, or something else.
If anyone is able to point-out the fundamental differences between the
configuration snippet that I posted previously and the snippet above,
which actually works, I would be most appreciative.

Thank you!

-Ben

On Wed, Jun 26, 2013 at 10:22:21AM -0400, Ben J. wrote:

Hi there,

I was able to accomplish my objective with some help from the tutorial
at:

It’s good that you’ve got it working now.

There are a few things that you might like to consider when deploying
it across all vhosts.

First: for a request, nginx picks one server and then one location
to process things in. So for this configuration to be common across
multiple servers, you either must copy-paste it into each; or else put
it in an external file and “include” that file in each per-server config
that matters.

location /pma {

Next, you probably want this to become “location ^~ /pma” – or maybe
even “location ^~ /pma/”, with a separate “location = /pma {return 301
/pma/;}” block.

http://nginx.org/r/location for the details of why that is.

(Hint: try to access /pmap, or /index.php, and you may see that things
go wrong.)

root /var/www/;
index index.php index.html index.htm;
location ~ ^/pma/(.+.php)$ {
try_files $uri =404;
root /var/www/;

That line, because it is identical to the enclosing one, probably does
nothing useful and can be removed.

   fastcgi_pass unix:/var/run/php5-fpm.sock;
   fastcgi_param HTTPS on;

You may be able to use the $https variable there, if you want to share
this config with both http and https servers and want to tell the
php server the truth about the original connection. But that’s not
critical here.

   fastcgi_index index.php;
   fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
   include /etc/nginx/fastcgi_params;

}
location ~* ^/pma/(.+.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt))$ {
root /var/www/;
}

That 3-line section, I don’t think does anything useful as-is.

With it, requests that match are served from the filesystem below
/var/www/; while requests that don’t match are served from the
filesystem
below /var/www/.

Without it, requests won’t match and so will be served from the
filesystem
below /var/www/.

}
location /PMA {
rewrite ^/* /pma last;
}

That will match both /PMA and /PMA/, which are probably what you want.

It will also match /PMAP, which may be unrelated; and it will match
/PMA/file.png and rewrite it to just /pma, which may not be wanted.

The rewrite itself could omit the “/*” part and have the same effect.

I would suggest actively redirecting to the correct /pma/ url, rather
than trying an internal rewrite – but again, if what you have works
for you, it’s good enough.

I don’t know if the key is the nested location block, or something else.
If anyone is able to point-out the fundamental differences between the
configuration snippet that I posted previously and the snippet above,
which actually works, I would be most appreciative.

You posted a few snippets previously. That last ones where you reported
404s, I failed to reproduce the 404s. They worked for me.

Possibly there was something else in the full configuration that meant
that the location{}s you showed were not the chosen ones for the
requests
you made?

f

Francis D. [email protected]

On 6/27/2013 12:42 PM, Ben J. wrote:

If I’m not mistaken, the location below would match all of the
customer’s own site), so I had assumed that I can’t use an empty
try_files $uri $uri/ @virtual;
fastcgi_intercept_errors on;
rewrite ^(.*)$ /index.php?q=$1 last;
}

A slight correction to part of my last reply.

The location block that actually worked to redirect requests to
incorrect variants of the /pma URL did contain the 301 redirect line:

location ~* /pma {
return 301 /pma/;
}

With this block, the “main site” seems to function as expected, and all
of the redirects that I identified previously work as expected.

Is this “incorrect” for any reason?

Thanks again,

-Ben

On 6/27/2013 12:42 PM, Ben J. wrote:

I don’t want PMA (anything within the /pma/ location) to be accessible
over a plaintext connection. In other words, I wish to force HTTPS.

Do I need to add something something like this to the location block?

rewrite ^ https://domain.com$request_uri? permanent;

(Ideally, I would like the “domain.com” part to be dynamic, so it works
for all vhosts; would I use $host, $server_name? Something else entirely?)

I ended-up with this, and it seems to work as expected:

location ^~ /pma/ {
root /var/www/;

if ($scheme = http) {
    return 301 https://$server_name$request_uri;
}

# ...

}

If there’s a better way of achieving the same, I’d love to be informed.

Thanks,

-Ben

On 6/26/2013 5:33 PM, Francis D. wrote:

it across all vhosts.

First: for a request, nginx picks one server and then one location
to process things in. So for this configuration to be common across
multiple servers, you either must copy-paste it into each; or else put
it in an external file and “include” that file in each per-server config
that matters.

That’s exactly what I’ve done; these directives reside within an include
file that I include from within each vhost’s main configuration.

Attempting to access /pmap yields a 404, and /index.php brings-up the
site’s primary index page (unrelated to PMA), but that may due to other
“location {}” blocks that ISPConfig (which I’m using to create each
vhost) includes in the nginx vhost configuration template.

In any case, what you say makes sense, and I see how using a regular
expression without a right-side anchor or a trailing slash could lead to
unexpected results, as your examples describe.

Do you happen to know if regular expressions in this context are
anchored, implicitly, if a least one explicit anchor is not specified?

root /var/www/;
index index.php index.html index.htm;
location ~ ^/pma/(.+.php)$ {
try_files $uri =404;
root /var/www/;

That line, because it is identical to the enclosing one, probably does
nothing useful and can be removed.

Are you referring to the two “root /var/www/” lines? If so, then what
you say makes sense, and I’ll remove the second/duplicate line.

   fastcgi_pass unix:/var/run/php5-fpm.sock;
   fastcgi_param HTTPS on;

You may be able to use the $https variable there, if you want to share
this config with both http and https servers and want to tell the
php server the truth about the original connection. But that’s not
critical here.

I don’t want PMA (anything within the /pma/ location) to be accessible
over a plaintext connection. In other words, I wish to force HTTPS.

Do I need to add something something like this to the location block?

rewrite ^ https://domain.com$request_uri? permanent;

(Ideally, I would like the “domain.com” part to be dynamic, so it works
for all vhosts; would I use $host, $server_name? Something else
entirely?)

With it, requests that match are served from the filesystem below
/var/www/; while requests that don’t match are served from the filesystem
below /var/www/.

Without it, requests won’t match and so will be served from the filesystem
below /var/www/.

I see; that makes sense, and I’ll remove that location block.

}
location /PMA {
rewrite ^/* /pma last;
}

That will match both /PMA and /PMA/, which are probably what you want.

It will also match /PMAP, which may be unrelated; and it will match
/PMA/file.png and rewrite it to just /pma, which may not be wanted.

Ah! Right you are, sir. /PMAP and /PMA/file.png are matched, which is
undesirable.

The rewrite itself could omit the “/*” part and have the same effect.

I would suggest actively redirecting to the correct /pma/ url, rather
than trying an internal rewrite – but again, if what you have works
for you, it’s good enough.

I’m not the type to accept “good enough”. I want it to be perfect :).

What would be your preferred course of action to eliminate the internal
rewrite and instead perform an external redirect for /pma, /PMA, and
/PMA/ (all redirected to /pma/)?

The documentation states, “There is no syntax for NOT matching a regular
expression. Instead, match the target regular expression and assign an
empty block, then use location / to match anything else.”

If I’m not mistaken, the location below would match all of the
“incorrect” variants that I listed above:

location ~* /pma {

}

But then I’m confused as to where the “return 301 /pma/;” needs to be
placed, if anywhere.

To be clear, I have a “normal website” running on this vhost (a
customer’s own site), so I had assumed that I can’t use an empty
location block, as the manual suggests, and fall-back to “location /”.

Somewhat surprisingly, including the above location block actually works
to redirect /pma, /PMA, and /PMA/ to /pma, while still allowing the
“normal site content” to function correctly.

Is this because I have the following directive just before the
PMA-related bit we’ve been discussing?

location / {
try_files $uri $uri/ @virtual;
}

location @virtual {
if ($uri !~ ‘/$’) {
return 301 $uri/$is_args$args;
}
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/lib/php5-fpm/web2.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors on;
rewrite ^(.*)$ /index.php?q=$1 last;
}

you made?

That is quite possible and very likely. I am adding my own directives to
ISPConfig’s nginx vhost configuration template, and at my limited level
of experience with nginx, I probably failed to realize that my location
blocks were being ignored in favor of another preferred match.

f

I really appreciate you sharing your time and expertise to get me
off-the-ground. I am learning a lot from you and I am profoundly
thankful for your detailed insights.

Respectfully,

-Ben

On 6/27/2013 1:15 PM, B.R. wrote:

>

If there's a better way of achieving the same, I'd love to be informed.

If is Evil… when used in location context | NGINX

I guess you can use the variables in the rewrite directive aswell.

Thanks,

-Ben

B. R.

Thanks for the feedback, B. R.

I resorted to the “if” control structure because, without it, the
browser became stuck in a redirect-loop. (The reason for this is
obvious.) Then again, I suppose that the redirect-loop would occur
regardless of whether I use a “return 301” or “rewrite”.

How would you recommend avoiding the redirect-loop problem?

I would really prefer no to have to define separate “server” blocks for
ports 80 and 443, due to the complex nature of the hosting environment
in which I’m working.

Currently, all vhosts receive:

server {
listen *:80;

listen *:443 ssl;

}

Without separating ports 80 and 443 into individual “server” blocks, I
saw no way to avoid the loop without using an “if” structure.

Am I missing something?

Thanks again,

-Ben

Hello again,

I resorted to the “if” control structure because, without it, the

browser became stuck in a redirect-loop. (The reason for this is
obvious.) Then again, I suppose that the redirect-loop would occur
regardless of whether I use a “return 301” or “rewrite”.

How would you recommend avoiding the redirect-loop problem?

​To me, it seems that if you have an infinite llop problem, the redirect
condition is not correct.
I would redirect URI starting with ‘http://’ to the same starting with
‘https://’. Once the ‘https’ location is requested, the redirect has no
effect anymore… with the supplemental benefit of Nginx automatically​

​sending a permanent 301 redirection and not a temporary 302 one (which
is
the default, unless explicitly stated otherwise, if I remember well)​.

I would really prefer no to have to define separate “server” blocks for

Without separating ports 80 and 443 into individual “server” blocks, I
saw no way to avoid the loop without using an “if” structure.

​IMHO, that’s the sorrect way of doing things. You could also listen for
IPv6 requests :o)

Am I missing something?

Thanks again,

-Ben


nginx mailing list
[email protected]
nginx Info Page


B. R.

Hello,

On Thu, Jun 27, 2013 at 1:02 PM, Ben J. [email protected]
wrote:

(Ideally, I would like the “domain.com” part to be dynamic, so it works
}

# ...

}

If there’s a better way of achieving the same, I’d love to be informed.


I would have kept the
rewritehttp://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewritestatement
which was a good idea.​

​The produced redirect is always a 301 (not 302) when the replacement
string starts with http(s) (check the doc page to which I provide the
link).​
Why would you replace it with a ‘if’ statement?

I guess you can use the variables in the rewrite directive aswell.

Thanks,

-Ben


nginx mailing list
[email protected]
nginx Info Page


B. R.

On Thu, Jun 27, 2013 at 12:42:34PM -0400, Ben J. wrote:

On 6/26/2013 5:33 PM, Francis D. wrote:

On Wed, Jun 26, 2013 at 10:22:21AM -0400, Ben J. wrote:

Hi there,

That’s exactly what I’ve done; these directives reside within an include
file that I include from within each vhost’s main configuration.

Good stuff.

Note that “include” is sort-of invisible to the rest of the nginx
configuration, so the rules for selecting the one location{} which
handles a request may lead to different results depending on where in
the file the “include” line is, if you use any regex location blocks.

(The easy way to avoid that is not to use any top-level regex
locations.)

Attempting to access /pmap yields a 404,

That’s what I expect. I suspect that even after you add the file
$document_root/pmap, it will still yield a 404, which may not be what
you expect.

and /index.php brings-up the
site’s primary index page (unrelated to PMA),

That is a bit of a surprise to me; but it does all depend on what
top-level location definitions appear in the server block.

In any case, what you say makes sense, and I see how using a regular
expression without a right-side anchor or a trailing slash could lead to
unexpected results, as your examples describe.

Do you happen to know if regular expressions in this context are
anchored, implicitly, if a least one explicit anchor is not specified?

I do; they aren’t; but these aren’t regular expressions. The characters
after the word “location” mean something. Check the docs if you’re not
clear on exactly which location is used for each request – it’ll make
further configuration understanding much easier.

That line, because it is identical to the enclosing one, probably does
nothing useful and can be removed.

Are you referring to the two “root /var/www/” lines? If so, then what
you say makes sense, and I’ll remove the second/duplicate line.

Correct. “root” is inherited, so setting it like this is unnecessary but
not incorrect. It only becomes incorrect if you change one and expect
the other to auto-change.

   fastcgi_pass unix:/var/run/php5-fpm.sock;
   fastcgi_param HTTPS on;

You may be able to use the $https variable there, if you want to share
this config with both http and https servers and want to tell the
php server the truth about the original connection. But that’s not
critical here.

I don’t want PMA (anything within the /pma/ location) to be accessible
over a plaintext connection. In other words, I wish to force HTTPS.

Ah, ok.

That fastcgi_param line merely tells the php server that https was used
to access this url. With $https, it would tell the php server whether
https was used to access this url.

Do I need to add something something like this to the location block?

Yes.

You’ve got the right answer in another mail.

What would be your preferred course of action to eliminate the internal
rewrite and instead perform an external redirect for /pma, /PMA, and
/PMA/ (all redirected to /pma/)?

Keep it simple.

location = /pma { return 301 /pma/; }
location = /PMA { return 301 /pma/; }
location = /PMA/ { return 301 /pma/; }

If I’m not mistaken, the location below would match all of the
“incorrect” variants that I listed above:

location ~* /pma {

That location would match any url that contains the four character
string,
case insensitive, “/pma”. Unless the url was already matched by another
^~
or ~ or ~* location.

To be clear, I have a “normal website” running on this vhost (a
customer’s own site), so I had assumed that I can’t use an empty
location block, as the manual suggests, and fall-back to “location /”.

The details matter.

One request, one location.

When you list all of the top-level locations in the server{}, then you
should be able to say which one will match a particular request.

(After considering rewrites.)

“empty location block” usually means “serve from the filesystem”.

Somewhat surprisingly, including the above location block actually works
to redirect /pma, /PMA, and /PMA/ to /pma, while still allowing the
“normal site content” to function correctly.

Does it also allow you to get at /pma/index.php? That would surprise me

but that’s because I’m guessing what the rest of the configuration is.

Is this because I have the following directive just before the
PMA-related bit we’ve been discussing?

Maybe.

The full list of the top-level location lines should make it clear to
you,
along with any server-level rewrite module directives.

I really appreciate you sharing your time and expertise to get me
off-the-ground. I am learning a lot from you and I am profoundly
thankful for your detailed insights.

You’re welcome.

Glad to be of help.

f

Francis D. [email protected]

On Thu, Jun 27, 2013 at 01:02:30PM -0400, Ben J. wrote:

On 6/27/2013 12:42 PM, Ben J. wrote:

Hi there,

I don’t want PMA (anything within the /pma/ location) to be accessible
over a plaintext connection. In other words, I wish to force HTTPS.

If there’s a better way of achieving the same, I’d love to be informed.
The usual suggestion is that different server configurations best live
in different server blocks.

You have other reasons not to want to do that here – including the
repetition aspect.

I’d say that what you have here is the best way.

f

Francis D. [email protected]

On Thu, Jun 27, 2013 at 12:55:22PM -0400, Ben J. wrote:

On 6/27/2013 12:42 PM, Ben J. wrote:

Hi there,

location ~* /pma {
return 301 /pma/;
}

With this block, the “main site” seems to function as expected, and all
of the redirects that I identified previously work as expected.

Is this “incorrect” for any reason?

If what you have works, it is correct enough.

Generally, the advice is that top-level (non-nested) regex locations
are bad because they tend to need more thinking when adding yet more
locations.

If you don’t intend to add more locations, then this badness is
irrelevant.

f

Francis D. [email protected]

On 6/27/2013 7:13 PM, Francis D. wrote:

in different server blocks.

You have other reasons not to want to do that here – including the
repetition aspect.

I’d say that what you have here is the best way.

f

Hi, Francis,

I’ve taken your good advice and swapped

location ~* /webmail {
return 301 /webmail/;
}

for

location = /webmail { return 301 /pma/; }
location = /WEBMAIL { return 301 /pma/; }
location = /WEBMAIL/ { return 301 /pma/; }

The latter form is simpler, its effect is obvious, and it seems less
prone to cause unexpected results if new locations are ever added.

On the whole, I’ve avoided top-level regex locations for the reason you
mentioned. Again, sound advice.

Everything else you say in your previous replies makes sense; you’ve
answered all of my questions (and then some!).

Excellent. Everything seems to be in good working order here.

Thanks again, Francis,

-Ben