96
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

m0meni
  • 16,006
  • 16
  • 82
  • 141
fmalina
  • 6,120
  • 4
  • 37
  • 47
  • 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 May 15 '09 at 14:33

3 Answers3

164

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.

Chuan Ma
  • 9,754
  • 2
  • 45
  • 37
  • i'm using wordpress, so should i need to put upload folder on nginx server ? – Chameron Jul 24 '15 at 02:07
  • 7
    My problem with this solution: nginx doesn't use the other location (the proxy) and tries to show the directory listing. So it throws a 403 because the directory listing is not activated. **Edit:** Ok if you remove `$uri/` then it works. – ChristophLSA Aug 24 '15 at 17:29
  • 1
    This would proxy /example to apache but allow /example.php to be downloaded. – Terence Johnson Sep 09 '15 at 13:16
  • 1
    @TerenceJohnson It's not safe to put your php files under the static file folder. Note that I set `root /path/to/root/of/*static*/files;`. php files should be put in a different folder, not directly accessible from internet. – Chuan Ma Sep 25 '15 at 18:14
  • 2
    I know that's the way things should be, but if someone is putting Nginx in front of any of the many common off-the-shelf PHP applications that have everything under the web root and rely on .htaccess files, they shouldn't copy and paste your configuration. – Terence Johnson Sep 25 '15 at 21:51
  • My answer is specific to this given question: nginx serves any file download requests on its own, and passes the remaining requests to apache. So if some one has a very old php app (with mixed static files and php files), they should not copy my config. Instead, they should be more specific about which files their nginx server can serve directly. – Chuan Ma Sep 28 '15 at 18:25
  • If the request is something like POST /sys/login, then you will get 405 not allowed error. – Donghua Liu Sep 19 '19 at 07:48
18

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.

Jasiu
  • 2,674
  • 2
  • 27
  • 27
6

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

lo_fye
  • 6,790
  • 4
  • 33
  • 49
  • This worked great, although i still get errors for minified files (e.g. site.min.css and main.min.js etc). How would I catch those in a location regex? @lo_fye – Garth Dec 20 '17 at 13:22