Rewrite URL to only show value of $_GET argument

Hello,

I have read through the nginx rewrite documentation and looked at
various
examples, but can’t figure out how to create a rewrite rule for the
following (if it is possible). I’d like to rewrite the URL of a php file
with a $_GET argument and replace it with just the value of the $_GET
argument. For example, I’d like to replace
/index.php?title=my_example_title with /my_example_title or
/article/my_example_title. I’ve tried several regular expressions to
match
index.php, as well as the $args and $arg_title nginx variables, but
cannot
get this working. For example:
rewrite ^/index.php?title=(.*)$ http://www.mysite.com/$1 redirect;

Can anyone provide inside into how to correctly rewrite this type of
URL?

Thanks,

Andrew

rewrite ^/index.php?title=(.*)$ http://www.mysite.com/$1 redirect;

this doesnt work? what is $1 then in the redirected request?

Posted at Nginx Forum:

On Mon, Sep 9, 2013 at 3:58 PM, mex [email protected] wrote:

rewrite ^/index.php?title=(.*)$ http://www.mysite.com/$1 redirect;

this doesnt work? what is $1 then in the redirected request?

of course this won’t work. Query string isn’t part of rewrite matching
string.

Use $arg_title instead.

http://nginx.org/en/docs/http/ngx_http_core_module.html#variables


O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Thanks for the suggestions. I was not able to get $arg_title to work.
Here
is the relevant section of my nginx config:
server_name mysite.com;

    try_files $uri $uri/ index.php;

    location / {
             rewrite ^/index\.php?title=(.*)$

http://mysite.com/$arg_title redirect;
}

    location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;

            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi_params;
    }

I also tried the rewrite rule inside of the “location ~ .php$” block,
but
it didn’t work there either. Visiting
mysite.com/index.php?title=my_test_page just loads that URL, it does not
redirect to mysite.com/my_test_page. Moreover, visiting
mysite.com/my_test_page results in a 404. What else should I try to make
this rewrite rule work?

Thanks,

Andrew

— Original message —
From: “Andrew Martin” [email protected]
Date: 9 September 2013, 15:53:01

May be something like this :
rewrite ^/index.php.* http://mysite.com/$arg_title redirect;
I think nginx “know” about all arguments in your request, therefore
simple specify needed argument’s name in the second part of rewrite
rule.

If I use this line:
rewrite ^/index.php(.*)$ http://mysite.com/$arg_title? redirect;

/index.php?title=my_test_page redirects to /my_test_page

This is the URL I am looking for, but it still results in a 404, with
this
displayed in the log:
[error] 16077#0: *156649 FastCGI sent in stderr: “Primary script
unknown”
while reading response header from upstream, client xxx.xxx.xxx.xxx,
server: mysite.com, request: “GET /my_test_page HTTP/1.1”, upstream:
“fastcgi://127.0.0.1:9000”, host: “mysite.com

Thanks,

Andrew

Francis,

On Tue, Sep 10, 2013 at 11:46 AM, Francis D. [email protected]
wrote:

/index.php?title=my_test_page&something=other
/index.php

? With the above code, the second will possibly redirect the way you want,
and the others probably won’t.

Would it be possible to only redirect if the title $_GET variable is
present?

Also, what should happen when I ask for

/my_test_page

? I will do that immediately after you redirect me there.

If you can describe the complete behaviour you want, the nginx
configuration needed to achieve it may become clear.

Thanks for clarifying this. The complete behavior I’m looking for is
just to
create SEF URLs for pages on the site by hiding the index.php?title=
part
of the URL. Thus, visiting /my_test_page in your browser would
internally
load the index.php?title=my_test_page URL but display the SEF URL to
the user. How can I achieve this behavior?

Thanks,

Andrew

On Mon, Sep 09, 2013 at 08:23:36AM -0500, Andrew Martin wrote:

Hi there,

If I use this line:
rewrite ^/index.php(.*)$ http://mysite.com/$arg_title? redirect;

/index.php?title=my_test_page redirects to /my_test_page

That’s what you asked for initially; I’d probably spell it as

location = /index.php {
return 302 http://mysite.com/$arg_title;
}

to make it clear what exactly is happening, and which might point out
the parts you didn’t specify:

what should happen if I ask for any of

/index.php?something=else
/index.php?title=my_test_page&something=other
/index.php

? With the above code, the second will possibly redirect the way you
want,
and the others probably won’t.

Also, what should happen when I ask for

/my_test_page

? I will do that immediately after you redirect me there.

If you can describe the complete behaviour you want, the nginx
configuration needed to achieve it may become clear.

f

Francis D. [email protected]

Francis,

Using the similar statement “try_files $uri $uri/ /index.php;”, if I
visit
this URL:
http://mysite.com/index.php?title=my_test_page
then the URL is rewritten to this, but it just loads the contents of
index.php (without the title variable):
http://mysite.com/my_test_page
What it shows would be equivalent to going to this page:
http://mysite.com/index.php

The part of my nginx configuration which communicates with php is:
location ~ .php$ {
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}

The php code is a custom page, not a pre-built CMS. It is doing an ajax
call to load the content, but should be functionally-equivalent to this:

<?php if (isset($_GET['title'])) { include($_GET['title'] . ".html"); } else { include("home.html"); } ?>

If I go to this page:
http://mysite.com/index.php?title=my_test_page
I would like the client’s browser to instead show this URL:
http://mysite.com/my_test_page

Does this help clarify what I am looking for?

Thanks,

Andrew

On Tue, Sep 10, 2013 at 09:07:46PM -0500, Andrew Martin wrote:

Hi there,

Would it be possible to only redirect if the title $_GET variable is
present?

Yes.

Use something like

if ($arg_title != “”) {
return 302 http://mysite.com/$arg_title;
}

inside the “location = /index.php” block, and then continue with
whatever
should happen if $arg_title is empty.

Thanks for clarifying this. The complete behavior I’m looking for is just to
create SEF URLs for pages on the site by hiding the index.php?title= part
of the URL. Thus, visiting /my_test_page in your browser would internally
load the index.php?title=my_test_page URL but display the SEF URL to
the user. How can I achieve this behavior?

I suspect that “try_files $uri /$uri /index.php;” might be enough for
what you ask for here; if it isn’t, then a description of what you do,
what you see, and what you expect to see, will probably make it easier
to understand where the problem is. (Your fastcgi-related configuration,
and the php code itself, will determine whether it is enough.)

If you search for nginx + your-php-application, do you see any
documentation on how to create SEF URLs? It may be easier than me
guessing here.

f

Francis D. [email protected]

On Wed, Sep 11, 2013 at 08:32:09AM -0500, Andrew Martin wrote:

Hi there,

Using the similar statement “try_files $uri $uri/ /index.php;”, if I visit
this URL:
http://mysite.com/index.php?title=my_test_page
then the URL is rewritten to this, but it just loads the contents of
index.php (without the title variable):

When you say “the contents of”, you mean “the unprocessed php”, yes?

In nginx, one request is handled in one location.

You must put all of the configuration that you wish to apply to a
request,
in the one location that handles that request.

This means that if you have a “location = /index.php”, then if the
request is for /index.php, your “location ~ php” will not be used.

In this case, can I suggest that you use a slightly different approach,
to keep separate things separate?

Something like (untested):

try_files $uri $uri/ @fallback;

location = /index.php {
# the browser requested /index.php
if ($arg_title != “”) {
return 302 http://mysite.com/$arg_title;
}
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
}

location @fallback {
# the browser requested something that is not on the nginx
filesystem
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param QUERY_STRING title=$uri;
include fastcgi_params;
}

but there will be rough edges there – you may want the “include” line
at
the start of the @fallback stanza rather than the end, and you probably
will need to tweak the QUERY_STRING param passed (to remove the leading
/
on $uri, most likely).

You can test to find what exactly is needed.

Probably enabling the debug log until you are certain that you
understand
what nginx is doing, will be helpful.

 include("home.html");

}
?>

Can I suggest that you test initially with that exact code, rather
than something that should be functionally equivalent? Keep it simple,
so that you can see at what point in the sequence things first fail.

(Even better: just do something like “print $_GET[‘title’]”, so you can
see exactly what you received. After that works right, add the
complexity.)

If “ajax” means “makes further http requests of nginx”, then you’ll need
to make sure that the first one works before trying the subsequent ones.

If I go to this page:
http://mysite.com/index.php?title=my_test_page
I would like the client’s browser to instead show this URL:
http://mysite.com/my_test_page

The “return” should do that.

What you now also want is for, when you go to
http://mysite.com/my_test_page, that nginx knows to tell the fastcgi
server to process the index.php page with certain arguments. For that,
you must configure nginx correctly.

Keep a very clear picture of how the browser, nginx, and the fastcgi/php
server communicate, and you’ll be able to work out where things are not
doing what you expect them to do; and then you may be able to see what
to change, to get the to do what you want.

Does this help clarify what I am looking for?

Building your own php framework, and making it work with nginx.

If you search for how every other framework does this, you may find
useful hints as to how your one will work.

Good luck with it,

f

Francis D. [email protected]

Francis,

I ended up coming up with this solution:
map $request_uri $request_basename {
~/(?<captured_request_basename>[^/?]*)(?:?|$)
$captured_request_basename;
}
server [

try_files $uri $uri/ @rewrite;

    location ~ [^/]\.php(/|$) {
            fastcgi_split_path_info ^(.+?\.php)(/.*)$;

            if (!-f $document_root$fastcgi_script_name) {
                    return 404;
            }

            fastcgi_pass 127.0.0.1:9004;
            fastcgi_index index.php;
            include fastcgi_params;
    }

    location @rewrite {
            if (!-e $request_filename) {
                    rewrite ^/article/[^/]+$

/index.php?title=$request_basename last;
}
}

}

This allows me to visit this URL in the browser:
http://mysite.com/article/my_test_page

and have nginx internally load this page:
http://mysite.com/index.php?title=my_test_page

Thanks again for all of your help,

Andrew

Steve,

Thanks for the suggestion. How would this additional check change the
solution I proposed on 9/16? It looks like it would prevent the rewrite
from occurring if other arguments (instead of title) were present?

Thanks again,

Andrew

I think you need to do some regexp on the args

if ( $args ~ title=([^&]+) {
rewrite ^(.)title=([^&]+).$ /article/$2? last;
}

Note… totally untested.

Steve

On Sun, 2013-09-08 at 23:01 -0500, Andrew Martin wrote:

to match index.php, as well as the $args and $arg_title nginx

Andrew


nginx mailing list
[email protected]
nginx Info Page


Steve H. BSc(Hons) MIITP

Linkedin: http://www.linkedin.com/in/steveholdoway
Skype: sholdowa