URI re-mapping using try_files

Hello,

I am new to nginx, and am still learning my way.

I use nginx to serve static html pages to users, and in particular, I
want
users who visit ‘www.example.com/public/doc/abc123?para=data’ to be
served
with the file ‘/home/www/example/public/doc/abc123/abc123.html’ on the
server, but ‘www.example.com/public/doc/abc123?para=data’ stays in the
user’s browser address bar.

So there are two issues here: 1. map URI pattern ‘/public/doc/blah’ to
file
‘/home/www/example/public/doc/blah/blah.html’ on the server; 2. keep
query
string ‘?parap=data’ in the address bar.

I wonder if these can be solved by using try_files within a location
block. For example, substring the $uri to take out ‘/blah’ part, and
append
it to the $uri followed by ‘.html’. And possibly append $args after
‘.html’?
How exactly do we do this in nginx? Thanks!

Posted at Nginx Forum:

On Thu, Oct 11, 2012 at 09:19:23AM -0400, mrtn wrote:

Hi there,

I use nginx to serve static html pages to users, and in particular, I want
users who visit ‘www.example.com/public/doc/abc123?para=data’ to be served
with the file ‘/home/www/example/public/doc/abc123/abc123.html’ on the
server, but ‘www.example.com/public/doc/abc123?para=data’ stays in the
user’s browser address bar.

For this sort of thing, “rewrite” (Module ngx_http_rewrite_module) is
probably
the directive you want.

    rewrite (.*)/(.*) $1/$2/$2.html break;

can do what you want in this case.

What do you want to happen if the user asks for any of:

http://www.example.com/public/doc/abc123
http://www.example.com/public/doc/abc123?para=nodata
http://www.example.com/public/doc/abc123/abc123.html
http://www.example.com/public/doc/one/two

The rewrite directive above may or may not do what you want for those.

So there are two issues here: 1. map URI pattern ‘/public/doc/blah’ to file
‘/home/www/example/public/doc/blah/blah.html’ on the server; 2. keep query
string ‘?parap=data’ in the address bar.

  1. is in the rewrite (or any similar configuration which maps url to
    file)

  2. is by virtue of not doing an external rewrite, which is a
    redirection.

f

Francis D. [email protected]

Hi Francis,

Thanks for introducing me to rewrite directive. Just to confirm, this is
how
I should use your rewrite:

root /home/www/example;

location /public/doc/ {
rewrite (.)/(.) $1/$2/$2.html break;
}

Ideally, for the other cases you raised, I want the following to happen:

http://www.example.com/public/doc/abc123
http://www.example.com/public/doc/abc123?para=nodata

when the query string (e.g. ?para=blah) part is missing or incomplete, I
want to serve a generic error page (e.g. /error.html)

http://www.example.com/public/doc/abc123/abc123.html

when the user tries to access the actual html page directly, I want to
block
it by either returning a 404 or serving a generic error page as above

http://www.example.com/public/doc/one/two

when the user queries an URI that has no corresponding .html file on the
server, I want to simply return a 404.

Can all these be implemented using rewrite only? Thanks.

Posted at Nginx Forum:

On Thu, Oct 11, 2012 at 03:59:24PM -0400, mrtn wrote:

Hi there,

Thanks for introducing me to rewrite directive. Just to confirm, this is how
I should use your rewrite:

root /home/www/example;

location /public/doc/ {
rewrite (.)/(.) $1/$2/$2.html break;
}

It can work outside of all locations, or else in the one location that
handles this request.

So this config can work.

Ideally, for the other cases you raised, I want the following to happen:

http://www.example.com/public/doc/abc123
http://www.example.com/public/doc/abc123?para=nodata

when the query string (e.g. ?para=blah) part is missing or incomplete, I
want to serve a generic error page (e.g. /error.html)

The above rewrite pays no attention to query strings.

So you’ll want to do something based on $arg_para – maybe an “if”
or something involving “map”.

I guess (without testing):

if ($arg_para != data) { return 404; }

inside that location{} would probably work.

http://www.example.com/public/doc/abc123/abc123.html

when the user tries to access the actual html page directly, I want to block
it by either returning a 404 or serving a generic error page as above

The above rewrite does that (assuming that the “rewritten” file is
absent).

http://www.example.com/public/doc/one/two

when the user queries an URI that has no corresponding .html file on the
server, I want to simply return a 404.

The above rewrite does that; but which html file should it look for
here?

/home/www/example/public/doc/one/two/two.html, or
/home/www/example/public/doc/one/two/one/two.html?

(As in: do you repeat everything after /public/doc, or do you repeat
just the final after-slash part?)

Can all these be implemented using rewrite only? Thanks.

With the extra “if” above, I think so.

Your testing should be able to show any problems, or unexpected
behaviour.

f

Francis D. [email protected]

On Thu, Oct 11, 2012 at 05:22:54PM -0400, mrtn wrote:

Hi there,

I guess (without testing):

if ($arg_para != data) { return 404; }

inside that location{} would probably work

Hmm, I read on nginx website and elsewhere that if statement may not work
consistently within a location directive, and is generally discouraged.
Should I worry in this case?

What precisely did you read? And does it apply here?

(I think I know the answers to those; but there’s no reason for you to
believe my words instead of words written elsewhere. If the words are
all consistent, then you can choose to believe them all. If the words
are
contradictory, then you must choose which source to consider incorrect.)

(As in: do you repeat everything after /public/doc, or do you repeat
just the final after-slash part?)

I only want to repeat the final after-slash part, so any
‘/public/doc/blahA/blahB’ should only look for
‘/public/doc/blahA/blahB/blahB.html’. Would that change anything in the
config proposed here?

What happened when you tried it?

mkdir -p /home/www/example/public/doc/blahA/blahB/blahA
echo This is in blahB >
/home/www/example/public/doc/blahA/blahB/blahB.html
echo This is in blahA >
/home/www/example/public/doc/blahA/blahB/blahA/blahB.html
curl -i http://www.example.com/public/doc/blahA/blahB

As it happens, the rewrite suggested should end up fetching the “in
blahB”
file, because it only repeats the part after the last slash.

When you understand why it does that, you’ll have a better chance of
writing your own regex-based rewrites.

Good luck with it!

f

Francis D. [email protected]

Hi Francis,

I guess (without testing):

if ($arg_para != data) { return 404; }

inside that location{} would probably work

Hmm, I read on nginx website and elsewhere that if statement may not
work
consistently within a location directive, and is generally discouraged.
Should I worry in this case?

The above rewrite does that; but which html file should it look for here?

/home/www/example/public/doc/one/two/two.html, or
/home/www/example/public/doc/one/two/one/two.html?

(As in: do you repeat everything after /public/doc, or do you repeat
just the final after-slash part?)

I only want to repeat the final after-slash part, so any
‘/public/doc/blahA/blahB’ should only look for
‘/public/doc/blahA/blahB/blahB.html’. Would that change anything in the
config proposed here?

Thanks again!

Mrtn

Posted at Nginx Forum: