Modifying an Nginx regular expression so that it is applied only in some cases

I'm creating a hybrid PHP/Node.js application. For PHP, I'm using the Yii framework. Within Node.js, I use express.

PHP is running through Nginx. I use the proxy_pass directive to funnel any request starting with /node to the Node server running on port 3000 (e.g. server.com/node/api is proxyed to server.com:3000/api.)

This works well, for the most part. However, one of the directives in my Nginx configuration file ensures that requests for non-existent static files are not passed on to Yii. Instead, a simple 404 error is returned. The original version of this directive looks like this:

location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar|txt)$ {
    try_files $uri =404;
    #For production servers you can set expires header with a long period
    expires 3M;
}

The problem with this directive is that some static files (notably the socket.io client) are served by Node.js. When the browser requests server.com/node/socket.io/socket.io.js, the above rule gets fired, and Nginx returns the 404.

What I want to do is modify the rule so that the 404 is returned only when the request for a static file is NOT preceded by /node/. I've tried various combinations of rules, but none of them seem to work. After consulting the perl regular expression documentation, here's one that I thought might work, but doesn't:

location ~* (?<!/node/)\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar|txt)$ {
    try_files $uri =404;
    expires 3M;
}

I thought the negative/look-behind part of the rule, (?<!/node/), would work to exclude requests starting with /node/, but it doesn't. Instead, the rule gets fired for both genuinely non-existent js files and for socket.io.js (and various other static files served from Node.js.) So in both cases I get a 404.

How should I change the regular expression so that the rule only gets applied when the request does NOT start with /node/?

If I read your question right, you're talking about paths that don't start with /node/. That's a lot simpler than matching something that's not preceded by something. This should do it:

location ~* ^(?!/node/).*\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar|txt)$ {

I think @Pogo almost had it, but as I understand it location doesn't include the server name. The regex start anchor, ^, matches the position immediately after .com (in this case) and before the first slash. Thus, ^(?!/node/) ensures that your path doesn't start with /node/.

The problem is, that the negative lookbehind looks immediately in front of the dot. Unfortunately, as far as I know, only .NET supports variable-length lookbehinds. A work around would be, reversing the string, reversing the regex and using a negative lookahead at the end (which can be variable-length):

(?!.*/node/)

Unfortunately, I cannot write out the full regex right now, because I am typing on a phone.

I think the problem might be because, javascipt doesn't support look behinds fully. You can look ahead at the start to see if it is not /node/ and then continue.

So,

.*com(?!/node/).*\.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar|txt)$

might help you.