tags:

views:

60

answers:

5

I want an nginx location directive that would match all urls with "xyz" and not match ".php"

I tried this

location ~* /(.*)xyz/(.*)(?!\.php)(.*)$ {}

but for example it always seem to match both /xyz/1 and /xyz/1.php but it should have matched only /xyz/1

+1  A: 

I don't think you can do this without a separate regex checking to make sure that ".php" is absent. The problem is that constructs like .* will swallow up enough characters to make sure that your negative lookahead never applies.

Michael Goldshteyn
+1  A: 

Well, as Michael said, though .*? (note the '?') will make the search non-greedy and it could work.

...Or, you could try (rather hackish):

[^.][^p][^h][^p]

Might works, might not... It definately is sort of a hack.

Tim Čas
This is difficult to read, debug and would require quite a bit of comment if left in application code. I think hacks should be reserved only for when there are no other solutions, and the solution is easy here.
Eric-Karl
This is wrong. It fails when at least one of the four character classes cannot be matched. But it should only fail if all of them cannot be matched.
Gumbo
I think you're mixing up `[^a][^b]` with `[^ab]`
Tim Čas
+2  A: 

You should be more specific about the begin and end of the location. If .php should not appear at the end of the location, put $ in the look-ahead assertion:

location ~* /(.*)xyz/(.*)(?!.*\.php$)(.*)$ {}

And if xyz should be a proper path segment and the first path segment (so it must start with /xyz/), use this:

location ~* ^/xyz/(?!.*\.php$)(.*)$ {}
Gumbo
I personally assume, given the (.*) at the end, that .php might end up with something else (parameters probably) after it. Yet the ^ and $ markers are often forgotten and may solve a lot of trouble when applicable. :D
Eric-Karl
+1  A: 

This one is simple, yet it can be tough to see. The second (.*) in the regex is "greedy", hence, it will capture everything including the ".php" so, to the parser's "eyes" there is no ".php" after it (it's already passed) and the result is a match. :(

Just make the second .* lazy by changing it to .*? and it will solve your problem :

location ~* /(.*)xyz/(.*?)(?!\.php)(.*)$ {}

I suggest you read this article, it explains it in a much better way than I. :D

Eric-Karl
A: 

Thanks all, we finally came up with this

location ~* ^/xyz([^.]*)/([^.]*)$ {
            rewrite  ^/xyz([^.]*)/([^.]*)$ /xyz$1/index.php/$2 last;
}

and it works.

rampr