nginx try_files, proxy_pass and upstream - php

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?

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.

Related

Proper handling of request_uri in double nginx reverse proxy?

So, essentially I am running Joomla in a Docker php7-fpm container, then I have an nginx container where a joomla.conf file is defined as follows:
#https://docs.joomla.org/nginx
server {
listen 8081;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
server_name php-docker.local;
root /usr/src/joomla;
index index.php index.html index.htm default.html default.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
# deny running scripts inside writable directories
location ~* /(images|cache|media|logs|tmp)/.*\.(php|pl|py|jsp|asp|sh|cgi)$ {
return 403;
error_page 403 /403_error.html;
}
location ~ \.php$ {
fastcgi_pass joomla:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
#include /etc/nginx/fastcgi.conf;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
And this works as expected... going to http://:8081 loads everything correctly.
Now, 8081 is just temporarily exposed in the nginx container, what I essentially want to do is setup a reverse proxy such that http:///joomla will be the final endpoint.
For this, I am struggling with the following conf file:
server{
listen 80;
server_name _;
location /joomla/ {
proxy_pass http://localhost:8081/;
proxy_set_header Referer $http_referer;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
}
}
What happens is that the HTML is served correctly, however, none of the assets are. This is because the URLs in Joomla are generated by a JURI class, which seems to rely on $request_uri, which by when it appears to arrive to Joomla is already lost.
https://github.com/joomla/joomla-cms/blob/6ab2a6e9010e7e04c260b9eba17dc76e866dd3e6/libraries/joomla/uri/uri.php#L87
So every link or reference to a file, script, or css renders like this:
http://localhost/login
http://localhost/images/headers/maple.jpg
Instead of:
http://localhost/joomla/login
http://localhost/joomla/images/headers/maple.jpg
However, when I access the second set of URL, I can access the link/asset without a problem... but of course once again, no images, templates, js or links being rendered correctly.
I prefer not to touch joomla.conf unless something is wrong, as for site.conf I would only like to translate URI segments to map requests to other applications, e.g.:
/joomla -> localhost:8081
/phpbb -> localhost:8082
/someapp -> localhost:8083
The cleanest solution would be to modify your upstreams to provide unique paths for all the resources.
It is generally a much easier task to remove parts of a URL from the upstream (with a unique prefix) than to add extra parts to a non-unique one. This is because you can always catch the longer URL and know exactly what it refers to, subsequently returning a 301 or 302 redirect to a shorter and more concise version.
On the other hand, faced with a short request URL, like /, it would be difficult to know for sure which app it may refer to (unless you look into the $http_referer variable, too, and then conditionally issue the redirect based on where the URL request comes from), or unless you implement some sort of spaghetti rules to detect which individual URLs refer to which applications (if you go this route, the map directive may come in handy).
Additionally, consider that, security-wise and when cookies get involved, it is not the best practice to run multiple independent applications on a single domain — a compromise in one application can easily lead to security violations in all the other ones.
As Rinos said, you need to configurate the $live_site var in configuration.php, as this:
public $live_site = '/joomla/';
I've made a complete example in Github that works well after editing that file, and it uses your configs.
 Becomes:
You could try use sub_filter how it mentioned in this answer.
So in your case your nginx config should looks like this one:
server{
listen 80;
server_name _;
location /joomla/ {
proxy_pass http://localhost:8081/;
proxy_set_header Referer $http_referer;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
sub_filter "http://your_server/" "http://your_server/joomla/";
sub_filter_once off;
}
}
I know this is old but here's my answer. You shouldn't change the configuration.php file, basically because you should keep your services (web and proxy) completely separated (A problem generated by a service implementation should be solved in that service), that will make your service architecture more flexible.
You have to filter the assets urls and some Location headers changing the following proxy settings.
server {
#... other settings
location /joomla/ {
# Main redirect
proxy_pass http://joomla_service/;
# Set the Host Header
proxy_set_header Host $host;
# When Joomla redirects a Location Header
# For example when you log in, Joomla retrieves you a response with a Location Header = localhost
# we don't want this.
proxy_redirect http://localhost/ http://localhost/joomla/;
proxy_redirect / /joomla/;
# Some content rewriting
sub_filter_types text/javascript application/javascript;
# Html redirection
sub_filter '="/' '="/joomla/';
sub_filter '="http://localhost/' '="/joomla/';
sub_filter '\'http://localhost/administrator/' '\'/joomla/administrator/';
sub_filter 'url(\'/media' 'url(\'/joomla/media';
# Some json data
sub_filter '"root":""' '"root":"\/joomla"';
sub_filter '"uri":"\/index.php' '"uri":"\/joomla\/index.php';
sub_filter '"jdragdrop":"\/media' '"jdragdrop":"\/joomla\/media';
sub_filter '"plg_quickicon_privacycheck_url":"http:\/\/localhost' '"plg_quickicon_privacycheck_url":"\/joomla';
sub_filter '"plg_quickicon_privacycheck_ajax_url":"http:\/\/localhost' '"plg_quickicon_privacycheck_ajax_url":"\/joomla';
sub_filter '"content_css":"\/templates' '"content_css":"\/joomla\/templates';
sub_filter_once off;
}
}
For some reason Joomla is not doing a good support if we talk about implement a reverse proxy. I created an example with more details, you can see it here.

how to correctly use url paths point to laravel apps using nginx?

I have the following nginx config
server {
listen 80;
client_max_body_size 2M;
server_name some.app;
root /var/virtual/a-cakephp-app/webroot;
access_log /var/log/nginx/a-cakephp-app-access.log;
include common.conf;
include cakephp.conf;
location /billing/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:89;
proxy_redirect off;
rewrite ^/billing/(.*)$ /$1 break;
}
And my webapps are:
/var/virtual/a-cakephp-app ==> virtual path that leads to the cakephp folder (definitely working)
/var/virtual/a-laravel-app ==> virtual path that leads to the laravel folder (not too sure how to test it)
What I want to achieve
I have a cakephp 2 app that is running at http://some.app. What I want is to start another app running Laravel at http://some.app/billing
My Laravel .env
APP_ENV=local
APP_DEBUG=true
APP_KEY=base64:somekey
APP_URL=http://some.app/billing
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
What did I get
I got a bad gateway error
What did I expect
I was hoping that the laravel app can work without compromising the cakephp app
It is generally a bad idea to mix and match independent apps on a single domain, which may lead to security issues w.r.t. cookies.
You're trying to use a proxy_pass for Laravel, a PHP framework. Note that this directive is meant to be used when the request is meant to be passed, in HTTP over TCP form, to a subsequent HTTP server, running on the specified port (e.g., Apache Tomcat, Jetty etc).
PHP (and hence Laravel) can be run by nginx itself, and doesn't require a separate server, so, your proxy_pass setup is likely a mistake, and should have been an appropriate fastcgi_pass set of directives instead (the normal way php is executed from within nginx).
We have no idea what is in the files that you include, however, it would be a good guesstimate that they do contain a location ~ \.php$ directive (for handling the php files of your existing php app).
Note that as per http://nginx.org/r/location, a location with a regular expression like location ~ \.php$ will take precedence over a prefix-string location like location /billing/, when a file like /billing/index.php is accessed.
To modify such behaviour, use the ^~ specifier for the prefix string location, e.g., location ^~ /billing/.
In summary, I'd use a separate domain. Else, use location ^~ /billing/, and fit all the proper fastcgi within.
You need to tell nginx that the root is different for /billing/ paths:
...
location /billing/ {
...
root /var/virtual/a-laravel-app/public;
# this replaces the rewrite
# rewrite will alter the url in nginx and a new lookup will be made
# the entry point for Laravel is the public/index.php file
try_files /index.php =404;
....
}
...
I kept the original config as such:
server {
listen 80;
client_max_body_size 2M;
server_name some.app;
root /var/virtual/a-cakephp-app/webroot;
access_log /var/log/nginx/a-cakephp-app-access.log;
include common.conf;
include cakephp.conf;
location /billing/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:89;
proxy_redirect off;
rewrite ^/billing/(.*)$ /$1 break;
}
Then I created this config after that:
server {
listen 89;
client_max_body_size 2M;
server_name 127.0.0.1;
root /var/virtual/a-cakephp-app/another-laravel-app/public;
include common.conf;
include cakephp.conf;
}
Note the port 89 does not matter so long as the server is not using it for some other app
Ideally you should not mix all the configurations under one file. nginx.conf should only only contain universal configurations like gzip being on, not to give out server tokens, etc. The individual file should be under sites-enabled folder
On why the bad gateway error is coming is maybe because you should have one root and then multiple location blocks to handle the same.
Also, once inside the billing block, are you trying to rewrite it to remove the billing folder? Why?

Redirect to wordpress blog through nginx

I am new to both, the nginx and the wordpress.
I have configured my project to be served through the nginx server. Now we need to add the wordpress blog support to our project.
My nginx is running over port 80. And the Apache is running over 8181 where my wordpress is installed.
Now any url going to blog is redirected to the apache over 8181 through my nginx server.
So below is my nginx redirect for the blog urls.
location /blog {
proxy_pass http://127.0.0.1:8181/blog/;
proxy_redirect https://192.168.1.54 http://127.0.0.1:8181/blog;
proxy_read_timeout 3500;
proxy_connect_timeout 3250;
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_set_header X-Forwarded-Proto https;
proxy_set_header SSL_PROTOCOL $ssl_protocol;
proxy_set_header SSL_CLIENT_CERT $ssl_client_cert;
proxy_set_header SSL_CLIENT_VERIFY $ssl_client_verify;
proxy_set_header SSL_SERVER_S_DN $ssl_client_s_dn;
}
For the Google page speed insight, I did below config.
location ~* \.(?:ico|css|js|gif|jpe?g|png|woff)$ {
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
}
Now, the problem I am facing here is that, when some css or js request comes from the wordpress eg. http://192.168.1.54/blog/wp-content/themes/twentysixteen/style.css?ver=4.6 is getting caught inside the location config written for css ie. location ~* .(?:ico|css|js|gif|jpe?g|png|woff)$ Hence wordpress is not able to get the css or js.
So, I need your help where the any URL requested for blog on the nginx should be redirected to Apache server on 8181 port. Including the js and css.
Where as other resource related url like https://192.168.1.54/js/somejs.js should also work.
I did some config change in wordpress file wp-config.php
define('WP_HOME', 'https://192.168.1.54/blog');
define('WP_SITEURL', 'https://192.168.1.54/blog');
You should use the ^~ modifier on the location /blog block to prevent conflict with regular expression location blocks at the same level. See this document for details.
location ^~ /blog {
proxy_pass http://127.0.0.1:8181;
...
}
Also, you should probably remove the URL element from the proxy_pass directive, because it looks like you are mapping /blog to /blog anyway.
And the proxy_redirect statement is back to front. But you probably do not need it as you have set WP_HOME/WP_SITEURL.

Why is nginx as apache front-end proxy not loading .html files?

I was following this tutorial https://www.digitalocean.com/community/articles/how-to-configure-nginx-as-a-front-end-proxy-for-apache to have a setup where nginx handles static stuff and all php files ares handled by apache on a fresh ubuntu box.
Everything went smoothly and I installed a php script on the server.
However, it is not loading any .html files. All .php files are loading and working fine. Whenever browser requests a html file, the browser kind of redirects back to index.php (although the url ending with .html remains in the address bar)
I have double checked and I've done everything according to the tutorial. What might prevent nginx from loading html files?
This is my nginx config:
server {
listen 80;
root /var/www/;
index index.php index.html index.htm;
server_name example.com;
location / {
try_files $uri $uri/ /index.php;
}
location ~ \.php$ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;
}
location ~ /\.ht {
deny all;
}
Below is the nginx error log:
http://d.pr/f/voxs

ZF: nginx to serve images/css/js/files and apache to serve .php

Hi i need to configure my webserver that runs a Zend Framework based Application:
My idea is to use:
nginx to serve images, files (zip/doc/xls/...), js ,html, ...
apache to serve the requests to zf actions
how can i setup it ?
thx a lot
Let's assume your Apache is running on port 8080 and your Nginx is running on port 80.
Your static files are located at /var/www/static/ and the folder structure there fully reflect HTTP requests expected.
Then (with some your context specific changes) it might be something like:
upstream zend {
server localhost:8080;
}
server {
listen 80;
server_name frontend;
location / {
alias /var/www/static/;
try_files $uri $uri/ #zend;
}
location #zend {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $proxy_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://zend;
}
}

Categories