Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nginx try_files, proxy_pass and upstream

I'm building a dockerised testing 'platform' for PHP apps - specifically (for the moment) for WordPress. I'm using PHPFarm to serve different versions of PHP on different ports. Using nginx in front, I've got much of it working. ( https://github.com/richardtape/testit is the main repo )

The big issue I'm facing now is getting WordPress's "pretty permalinks" to work. In a standard nginx setup, it's just a case of something like

location / {
    index index.php index.html index.htm;
    try_files $uri $uri/ /index.php?$args;
}

But in order to be able to have nice urls from the host machine, and in order to have one code base, I'm using something along the following lines:

server {
    listen 80;
    index index.php index.html index.htm;
    server_name 52.spaces.dev;

    location / {

        proxy_pass http://phpfarm_52;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
    root /var/www;
}

upstream phpfarm_52{
    server phpfarm:8052;
}

This, as it stands, works. (There are 5 more rules similar for this for PHP 5.3, 5.4, 5.5, 5.6 and 7) The home page loads on each of the different server_names from the host machine (and if you output the PHP version on each of them, you can see that you're getting a different PHP version).

However, the second I switch to an 'internal' url (or any non-root i.e. http://52.spaces.dev/about/), I get a 404. I've tried something similar to

location / {
    try_files $uri $uri/ /index.php?$args
}

location ~ \.php$ {
    proxy_pass http://phpfarm_52;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
}

I get a redirect loop, depending on a few different ways I've tried it's either just been a series of 301 redirects and the page never loads or an error such as

nginx_1      | 2016/04/08 20:31:29 [error] 5#5: *4 rewrite or internal redirection cycle while processing "/index.php", client: 192.168.99.1, server: 52.spaces.dev, request: "GET /favicon.ico HTTP/1.1", host: "52.spaces.dev", referrer: "http://52.spaces.dev/"

I'm stuck. I'm also pretty new to nginx configuration (which may be obvious) so I might well be doing something completely wrong and/or dumb. Any suggestions?

like image 537
RichardTape Avatar asked Apr 08 '16 22:04

RichardTape


People also ask

What does proxy_pass do in Nginx?

The proxy_pass setting makes the Nginx reverse proxy setup work. The proxy_pass is configured in the location section of any virtual host configuration file. To set up an Nginx proxy_pass globally, edit the default file in Nginx's sites-available folder.

What does try_files do in Nginx?

Using try_files means that you can test a sequence. If $uri doesn't exist, try $uri/ , if that doesn't exist try a fallback location.

What is try_files $Uri index HTML?

The try_file directive is in the server and location blocks and specifies the files and directories in which Nginx should check for files if the request to the specified location is received. A typical try_files directive syntax is as: location / { try_files $uri $uri/ /default/index.html; }

What is Proxy_set_header Nginx?

To adjust or set headers for proxied connections, use the proxy_set_header directive, followed by the header value. You can find a list of all available Request Headers and their allowed values here . If you want to prevent a header from being passed to the proxied server, set it to an empty string "" .


1 Answers

The issue you're experiencing with the redirect loop in your question is that basically every request, even for static files tries to route via your index.php?$args block.

I see 2 possible solutions here. First, if you are willing to implement the nginx setup with a single nginx instance, look at this thread: NGINX try_files with multiple named locations and this blog post http://linuxplayer.org/2013/06/nginx-try-files-on-multiple-named-location-or-server

What needs to happen, is you need to first test if the resource is present on the upstream as is (i.e., doesn't return 404). If it does, then you serve it as is. If it does not, then you would be calling the rewrite block which tries to put it as a parameter to index.php. So you would end up with something like this (sorry, I don't really have a chance to test this, but hopefully it gives an idea):

location / {
    try_files $uri $uri/ @phpproxy;
}

location @phpproxy {
    proxy_pass http://phpfarm_52;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_intercept_errors on;
    recursive_error_pages on;
    error_page 404 = @rewrite_proxy;
}

location @rewrite_proxy {
    rewrite ^/(.*)$ /index.php?$1 break;
    proxy_pass http://phpfarm_52;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
}

The second solution (that I would personally prefer) is to front every upstream with its own nginx. Then the top nginx which is fronting all others will have a much cleaner structure with simple proxy_pass's (+ maybe some static content) and that's it. It will also cut down on the request round-trip, since you wouldn't need to resolve 404's coming from the upstreams.

like image 184
taleodor Avatar answered Sep 19 '22 08:09

taleodor