92
votes
location / {
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    if (-f $request_filename) {
        access_log off;
        expires 30d;
        break;
        }

    if (!-f $request_filename) {
        proxy_pass http://127.0.0.1:8080; # backend server listening
        break;
        }
    }

Above will serve all existing files directly using Nginx (e.g. Nginx just displays PHP source code), otherwise forward a request to Apache. I need to exclude *.php files from the rule so that requests for *.php are also passed to Apache and processed.

I want Nginx to handle all static files and Apache to process all dynamic stuff.

EDIT: There is white list approach, but it is not very elegant, See all those extensions, I don't want this.

location ~* ^.+.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js)$ {
    access_log off;
    expires 30d;
    }
location / {
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:8080;
    }

EDIT 2: On newer versions of Nginx use try_files instead http://wiki.nginx.org/HttpCoreModule#try_files

3
Just to clear things: above code will serve a static file if it exists on the disk, if the file does not exists the request is passed to Apache. This works most of the time as all URLs in my applications use mod_rewrite (or routing) and do not really exist on the disk. Only the direct access to *.php filename is exception and needs to be parsed by Apache.fmalina

3 Answers

159
votes

Use try_files and named location block ('@apachesite'). This will remove unnecessary regex match and if block. More efficient.

location / {
    root /path/to/root/of/static/files;
    try_files $uri $uri/ @apachesite;

    expires max;
    access_log off;
}

location @apachesite {
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:8080;
}

Update: The assumption of this config is that there doesn't exist any php script under /path/to/root/of/static/files. This is common in most modern php frameworks. In case your legacy php projects have both php scripts and static files mixed in the same folder, you may have to whitelist all of the file types you want nginx to serve.

18
votes

Try this:

location / {
    root /path/to/root;
    expires 30d;
    access_log off;
}

location ~* ^.*\.php$ {
    if (!-f $request_filename) {
        return 404;
    }
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:8080;
}

Hopefully it works. Regular expressions have higher priority than plain strings, so all requests ending in .php should be forwared to Apache if only a corresponding .php file exists. Rest will be handled as static files. The actual algorithm of evaluating location is here.

6
votes

If you use mod_rewrite to hide the extension of your scripts, or if you just like pretty URLs that end in /, then you might want to approach this from the other direction. Tell nginx to let anything with a non-static extension to go through to apache. For example:

location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$
{
    root   /path/to/static-content;
}

location ~* ^!.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$ {
    if (!-f $request_filename) {
        return 404;
    }
    proxy_set_header X-Real-IP  $remote_addr;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:8080;
}

I found the first part of this snippet over at: http://code.google.com/p/scalr/wiki/NginxStatic