# Rewrite before regex location

**URL:** <https://www.ruby-forum.com/t/rewrite-before-regex-location/244569>\
**Category:** NGINX\
**Created:** [May 4, 2016, 11:27pm UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569 "2016-05-04T23:27:05Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Rafa\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/958977/32.png) [@Rafa\_F](https://www.ruby-forum.com/u/Rafa_F)\
**Post date:** [May 4, 2016, 11:27pm UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/1 "2016-05-04T23:27:05Z")

</div>

I am trying to migrate to Nginx + PHP-FPM from Apache + mod\_php. I am  
currently using mod\_rewrite to rewrite some virtual URIs ending in .php  
to  
actual PHP scripts. This works perfectly when using mod\_php. But with  
with Nginx

- FPM, since we have to use proxy\_pass, this is not working. When I add  
a  
regex location block to match .php extension, it gets higher precedence,  
and my rewrite rules are not applied.

How can I resolve this?

location /test/ {  
rewrite “^/test/([a-z]+).php$” test.php?q=$1 last; }

location ~ [^/].php(/|$) {  
fastcgi\_split\_path\_info ^(.+?.php)(/.\*)$;

```
set $fastcgi_script_name_custom $fastcgi_script_name;
if (!-f $document_root$fastcgi_script_name) {
    set $fastcgi_script_name_custom "/cms/index.php";
}

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

```

Thank,  
Joyce B.

NB: This is a sample configuration. There are several such rewrite  
rules,  
hence I cannot use a exact match or regular expression location block to  
override the php proxy location block.

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 5, 2016, 12:00am UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/2 "2016-05-05T00:00:58Z")

</div>

On Thu, May 05, 2016 at 02:56:27AM +0530, Joyce B. wrote:

Hi there,

> When I add a  
> regex location block to match .php extension, it gets higher precedence,  
> and my rewrite rules are not applied.
> 
> How can I resolve this?
> 
> location /test/ {  
> rewrite “^/test/([a-z]+).php$” test.php?q=$1 last; }

Possibly using “location ^~ /test/” would work?

> **[Module ngx\_http\_core\_module](http://nginx.org/en/docs/http/ngx_http_core_module.html#location)**
>
> This directive appeared in version 1.11.8.

You may want to rewrite to /test.php (with the leading slash), though.

Although, better might be to just fastcgi\_pass directly, rather than  
rewriting.

Something like (untested)

location ^~ /test/ {  
fastcgi\_param SCRIPT\_FILENAME $document\_root/test.php;  
fastcgi\_param QUERY\_STRING q=$uri;  
include fastcgi\_params;  
fastcgi\_pass 127.0.0.1:9000;  
}

although you would want to change the QUERY\_STRING line to match what  
you need; and you may need to switch the position of the “include”,  
depending on how your fastcgi server handles repeated params.

> location ~ [^/].php(/|$) {  
> fastcgi\_split\_path\_info ^(.+?.php)(/.\*)$;
> 
> ```
> set $fastcgi_script_name_custom $fastcgi_script_name;
> if (!-f $document_root$fastcgi_script_name) {
> set $fastcgi_script_name_custom "/cms/index.php";
> }
> 
> ```

I suspect that it should be possible to do what you want to do there,  
with a “try\_files”. But I do not know the details.

Good luck with it,

## f

Francis D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 5, 2016, 12:52am UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/3 "2016-05-05T00:52:58Z")

</div>

Hi Francis,

Thank you for the response.

Possibly using “location ^~ /test/” would work?

> location ^~ /test/ {  
> fastcgi\_param SCRIPT\_FILENAME $document\_root/test.php;  
> fastcgi\_param QUERY\_STRING q=$uri;  
> include fastcgi\_params;  
> fastcgi\_pass 127.0.0.1:9000;  
> }
> 
> although you would want to change the QUERY\_STRING line to match what  
> you need; and you may need to switch the position of the “include”,  
> depending on how your fastcgi server handles repeated params.

There are over 300 rewrites under 54 location blocks.

I tried using ^~ as you suggested. Now the rewrite is working correctly,  
but the files are not executed. The request is returning the actual PHP  
source file, not the HTML generated by executing the script.

> 

There is a CMS engine which will intercept all unmatched requests and  
check  
the database to see if there is an article with that URI. Some times it  
has  
to match existing directories without index.php. If I use try\_files, it  
will either lead to a 403 error (if no index is specified), or would  
internally redirect the request to the index file (if it is specified),  
leading to 404 error. The if condition correctly handles all the  
non-existing files.

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 5, 2016, 1:15am UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/4 "2016-05-05T01:15:45Z")

</div>

On Thu, May 05, 2016 at 04:22:11AM +0530, Joyce B. wrote:

Hi there,

> > Possibly using “location ^~ /test/” would work?

> There are over 300 rewrites under 54 location blocks.

If you’ve got a messy config with no common patterns, you’ve got a messy  
config with no common patterns, and there’s not much you can do about  
it.

If you can find common patterns, maybe you can make the config more  
maintainable (read: no top-level regex locations); but you don’t want  
to break previously-working urls.

> I tried using ^~ as you suggested. Now the rewrite is working correctly,  
> but the files are not executed. The request is returning the actual PHP  
> source file, not the HTML generated by executing the script.

Can you show one configuration that leads to the php content being  
returned?

If you rewrite /test/x.php to /test.php, /test.php should be handled in  
the “~ php” location.

An alternative possibility could be to put these rewrites at server  
level rather than inside location blocks. That is unlikely to be great  
for efficiency; but only you can judge whether it could be adequate.

> There is a CMS engine which will intercept all unmatched requests and check  
> the database to see if there is an article with that URI. Some times it has  
> to match existing directories without index.php. If I use try\_files, it  
> will either lead to a 403 error (if no index is specified), or would  
> internally redirect the request to the index file (if it is specified),  
> leading to 404 error. The if condition correctly handles all the  
> non-existing files.

There is more than one possible try\_files configuration; but that does  
not  
matter: if you have a system that works for you, you can keep using it.

Good luck with it,

## f

Francis D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 5, 2016, 1:44am UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/5 "2016-05-05T01:44:22Z")

</div>

> If you’ve got a messy config with no common patterns, you’ve got a messy  
> config with no common patterns, and there’s not much you can do about it.
> 
> If you can find common patterns, maybe you can make the config more  
> maintainable (read: no top-level regex locations); but you don’t want  
> to break previously-working urls.

The site was initially using Apache + mod\_php. Hence these ere not an  
issue. It was only when  
I tried to migrate to PHP-FPM, I realized the mistakes. Now the urls  
cannot  
be chanced due to  
SEO implications.

> 

I am sorry, I did not rewrite it to a location outside /test/, which was  
why the file content was being returned.

Is it possible to do something like this?

location /test/ {  
rewrite “^/test/([a-z]+).php$” /php-fpm/test/test.php?q=$1 last;  
}

location ~ ^/php-fpm/ {  
location ~ [^/].php(/|$) {  
fastcgi\_split\_path\_info ^/php-fpm(.+?.php)(/.\*)$;

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

```

}

What I have tried to do here is rewrite to add a special prefix  
(/php-fpm)  
to the rewritten urls. and nest the php location block within it. Then  
use  
fastcgi\_split\_path\_info to create new $fastcgi\_script\_name without the  
special prefix. I tried the above code, but it is not working.  
fastcgi\_split\_path\_info is not generating $fastcgi\_script\_name without  
the  
/php-fpm prefix.

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 5, 2016, 6:44pm UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/6 "2016-05-05T18:44:53Z")

</div>

On Thu, May 05, 2016 at 05:13:29AM +0530, Joyce B. wrote:

Hi there,

> ```
> fastcgi_pass 127.0.0.1:9000;
> 
> ```
> 
> fastcgi\_split\_path\_info is not generating $fastcgi\_script\_name without the  
> /php-fpm prefix.

That’s because your fastcgi\_split\_path\_info pattern does not match -  
.php is not followed by / in your rewritten url.

Because of the location{} you are in, it is probably simplest to just  
replace the second capture part of that pattern with (.\*)$.

Cheers,

## f

Francis D. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 5, 2016, 4:22am UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/7 "2016-05-05T04:22:40Z")

</div>

Hi ,

Can you try putting the rewrite in server{} block outside of all  
location{} blocks like

rewrite “^/test/([a-z]+).php$” /test/test.php?q=$1 last;

On Thu, May 5, 2016 at 5:13 AM, Joyce B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I tried to migrate to PHP-FPM, I realized the mistakes. Now the urls cannot
> 
> > returned?  
> > location /test/ {  
> > }
> 
> > > > > ```
> > > > > set $fastcgi_script_name_custom "/cms/index.php";
> > > > > 
> > > > > ```
> > > 
> > > will either lead to a 403 error (if no index is specified), or would  
> > > –  
> > > nginx mailing list  
> > > [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
> > > [mailman.nginx.org archive](http://mailman.nginx.org/mailman/listinfo/nginx)

–  
Anoop P Alias

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 6, 2016, 10:17pm UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/8 "2016-05-06T22:17:29Z")

</div>

Thank you for the suggestion, Anoop. I did not want to do that since it  
would be evaluated for every request.

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 6, 2016, 1:52pm UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/9 "2016-05-06T13:52:46Z")

</div>

As a sidenote, why using  
location ~ ^/php-fpm/  
and not  
location /php-fpm/  
?

## Although being distant to your case (I see others are helping you nicely :o) ), I can but particularly note & enjoy the use of a non-greedy modifier in the regex part of fastcgi\_split\_path\_info intended to result in $fastcgi\_script\_name. That detail means a lot. Keep up the good work :o) ​​

_B. R._

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 6, 2016, 10:36pm UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/10 "2016-05-06T22:36:46Z")

</div>

> That’s because your fastcgi\_split\_path\_info pattern does not match -  
> .php is not followed by / in your rewritten url.
> 
> Because of the location{} you are in, it is probably simplest to just  
> replace the second capture part of that pattern with (.\*)$.

Thank you Francis. It worked after following your suggestions.

But I have found a different approach, that is working for all the cases  
that I tested. As per your suggestions, I have removed the if statement  
with try\_files and have used the ~^ modifier to ensure that the location  
blocks get higher precedence than the regex block.

1. index index.php;
2. 
3. location ~ /.ht {
4. 

```
deny all;

```

5. }
6. 
7. 
# .php catchall regex block
8. location ~ [^/].php$ {
9. 

```
# Handle .php uris, except when they are overridden below

```

10. 

```
include inc/php-fpm.conf;

```

11. }
12. 
13. location / {
14. 

```
if ($request_uri ~ "^[^?]*?//") {

```

15. 

```
    # Remove double slashes from url

```

16. 

```
    rewrite "^" $scheme://$host$uri permanent;

```

17. 

```
}

```

18. 
19. 

```
# Handle non .php files and directory indexes

```

20. 

```
try_files $uri $uri/index.php;

```

21. }
22. 
23. location /generated/imgs/ {
24. 

```
# Serve file if it exists, else use rewriting (for 

```

dynamically  
generated and cached files)  
25. # This ensures that rewrite rules are not applied to existing  
files  
26. try\_files $uri @rewrite\_generated\_imgs;  
27. }  
28.  
29. location @rewrite\_generated\_imgs {  
30. # Since extension is changed from .jpg to .php, use last.  
31. # This will pass the rewritten uri to the .php regex block  
for  
execution  
32.  
33. rewrite “^/generated/imgs/([0-9]+)/(._).jpg$"  
/generated/imgs/pic.php?width=$1&height=$1&pic=$2&mode=proportionate  
last;  
34. rewrite "^/generated/imgs/([0-9]+)x([0-9]+)/(._).jpg$”  
/generated/imgs/pic.php?width=$1&height=$2&pic=$3&mode=exact last;  
35.  
36. try\_files $uri $uri/index.php;  
37. }  
38.  
39. # The ^~ modifier ensures that .php uris are not caught by the  
.php  
catchall regex block  
40. location ^~ /banking/ {  
41. rewrite “^/banking/sitemap-(\d+).xml.gz$”  
/banking/sitemap-generator.php?id=$1 last;  
42.  
43. try\_files $uri $uri/index.php;  
44.  
45. location ~ [^/].php$ {  
46. rewrite “^/banking/([A-Za-z0-9-]+)/branches.php$”  
/banking/branches.php?name=$1 break;  
47. rewrite “^/banking/([A-Za-z0-9-]+)/atm.php$”  
/banking/atm.php?name=$1 break;  
48.  
49. include inc/php-fpm.conf;  
50. }  
51. }  
52.  
53. location ^~ /news/ {  
54. # Check for file before applying rewrite rules. If not,  
rewrite  
55. # This ensures that rewrite rules are not applied to existing  
files  
56. try\_files $uri $uri/index.php @rewrite\_news;  
57. }  
58.  
59. location @rewrite\_news {  
60. # Rewrite non PHP urls in outer location block.  
61. # Use last so that it will be passed to the .php regex block  
after rewrite.  
62. # This ensures that the php code is executed and not  
downloaded  
as is  
63.  
64. rewrite “^/news/articles/?$” /news/ permanent;  
65. rewrite “^/news/articles/a([0-9]+).html$”  
/news/article.php?id=  
$1&mode=article last;  
66. rewrite “^/news/page-([0-9]+).html$” /news/index.php?cat=  
home&page=$1 last;  
67. rewrite “^/news/([a-z-]+)/rss.xml$” /news/rss/$1.xml break;  
68.  
69. try\_files $uri $uri/index.php;  
70.  
71. location ~ [^/].php$ {  
72. # Rewrite PHP urls  
73. # Use break so that the rewrite rules are not applied  
again  
74.  
75. rewrite “^/news/print.php$” /news/article.php?mode=print  
break;  
76.  
77. include inc/php-fpm.conf;  
78. }  
79. }  
80.  
81. ================  
82. # inc/php-fpm.conf  
83.  
84. # /cms/cms.php is the fallback script that will handle all  
missing  
uris  
85. try\_files $uri /cms/cms.php =404;  
86.  
87. fastcgi\_pass 127.0.0.1:9000;  
88. fastcgi\_index index.php;  
89. include fastcgi\_params;

Does this look fine? Is it a bad idea to use named locations like this?

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 7, 2016, 12:25am UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/11 "2016-05-07T00:25:26Z")

</div>

It is working fine on my mac. But when I try to use it on my Cent OS  
server, it is failing with error

nginx: [emerg] location “[^/].php$” cannot be inside the named location  
“@rewrite\_news”

Both are running version 1.10.0 ☹

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 7, 2016, 12:28am UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/12 "2016-05-07T00:28:56Z")

</div>

Oops! The local configuration was _location ^~ @rewrite\_news {…}_

---

<div class="post-metadata">

**Author:** ![Joyce\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/46a35a/32.png) [@Joyce\_B](https://www.ruby-forum.com/u/Joyce_B)\
**Post date:** [May 7, 2016, 12:29am UTC](https://www.ruby-forum.com/t/rewrite-before-regex-location/244569/13 "2016-05-07T00:29:31Z")

</div>

See [Module ngx\_http\_core\_module](http://nginx.org/en/docs/http/ngx_http_core_module.html#location:)

‘The “@” prefix defines a named location. Such a location is not used  
for a  
regular request processing, but instead used for request redirection.  
They  
cannot be nested, and cannot contain nested locations.’
