This is what needs to be done to get a reverse proxy working with client requesting data from Nginx at a 'sub-directory' location and an Apache backend serving the application from the root-directory
In hindsight this was probably what I would need to do in order to get Outlook Web Access or similar to work with NGinx as these applications embed a lot of resource URL's that don't play nicely with a custom or odd reverse proxy config
I learn't not everything can be fixed with URL and request rewriting. Sometimes the response needs to be hacked too.
I have an Apache Web Server serving a CakePHP application using the CakePHP webroot as the Apache Document root as follows. A request to the Apache server for / will return the CakePHP default route.
1 2 3 4 5 6 7 8 9 10 11 12 | <VirtualHost *:80> RewriteEngine On DocumentRoot /var/www/wms/app/webroot <Directory /> Options +FollowSymLinks Options +SymLinksIfOwnerMatch AllowOverride All Order allow,deny AllowOverride All Allow from all </Directory> </VirtualHost> |
I then have a Nginx server acting as a reverse proxy. I want it to serve a CakePHP Application from a subdirectory location. In the following example each request for /tgn/* needs to have tgn stripped from it before it is passed to the backend
1 2 3 4 5 6 7 8 9 | location /tgn/ { rewrite ^/tgn(/.*)$ $1 break; proxy_pass http://tgn-img/; proxy_set_header Accept-Encoding ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_redirect http://localhost/ http://$http_host/tgn/; } |
The rewrite line does a good job of making sure any request to /tgn/.* gets rewritten for the back end server. The problem is the pages that get served have embedded links in the them pointing to the wrong place. So you may get an index page but all the js, css, images and other items are broken.
So you need to rewrite each response to make sure all the embedded links have /tgn/ prepended to them
Here is my nginx /etc/nginx/conf.d/default.conf
file. Showing examples of using the http_sub_module
to find and replace values to make sure the pages request resources from the correct URL. Note the comments.
Check you have the http_sub_module by running nginx -V
and looking for --with-http_sub_module
in the output.
Create sub_filter rules with a find and replace snippets of anything in the HTML pages that would need changing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | server { listen 80; server_name localhost; # sub_filter_types '*'; #charset koi8-r; access_log /var/log/nginx/host.access.log main; location /tgn/ { # these sub_filter rules # scan the text/html response from the backend # and find and replace them to make them work # with the above location # you need to use your dev tools in your browser # and find all the 404's and other weird errors # and then track them down and add a rule here # all the image src elements, css & js sources # need changing sub_filter 'src="/files/' 'src="/tgn/files/'; sub_filter '<link href="/img/' '<link href="/tgn/img/'; sub_filter 'href="/css/' 'href="/tgn/css/'; sub_filter 'src="/img/' 'src="/tgn/img/'; sub_filter 'src="/js/' 'src="/tgn/js/'; # embedded data-* elements need changing sub_filter 'data-queryurl="/items/' 'data-queryurl="/tgn/items/'; # Form Submit actions need re-writing sub_filter 'action="/Labels' 'action="/tgn/Labels'; # Links to CakePHP Controllers re-writtern sub_filter 'href="/Labels' 'href="/tgn/Labels'; sub_filter 'href="/Shipments' 'href="/tgn/Shipments'; sub_filter 'href="/PrintLabels' 'href="/tgn/PrintLabels'; sub_filter 'href="/Items' 'href="/tgn/Items'; sub_filter 'href="/Locations' 'href="/tgn/Locations'; sub_filter 'href="/Menus' 'href="/tgn/Menus'; sub_filter 'href="/Users' 'href="/tgn/Users'; sub_filter 'href="/Settings' 'href="/tgn/Settings'; sub_filter 'href="/ProductTypes' 'href="/tgn/ProductTypes'; sub_filter 'href="/Shifts' 'href="/tgn/Shifts'; sub_filter 'href="/PrintTemplates' 'href="/tgn/PrintTemplates'; sub_filter 'href="/LabelHistories' 'href="/tgn/LabelHistories'; sub_filter 'href="/PackSizes' 'href="/tgn/PackSizes'; # code injection too! sub_filter '</head>' '<script>/path/to/code.js</script></head>'; # if you don't turn this off # the http_sub_module will only replace # the first instance it finds # this makes the find and replace global sub_filter_once off; # this rewrite rule is the core of getting nginx # to serve a backend application from a subdirectory # requests to nginx at /tgn/ goto the backend at / rewrite ^/tgn(.*)$ $1 break; # the break command says once you do this # rewrite continue on with the next # directives inside this block. i.e. # pass the request to the backend # # if break was replaced with 'last' # processing would stop and exit the location # block and then try and rematch the # request to a location. See the docs I'm # spit balling here. # since I was using docker containers # tgn-img is the name of the Apache container # which held the CakePHP app proxy_pass http://tgn-img; # ***** IMPORTANT ******* # without this Accept-Encoding the Apache Server # sends back compressed content and the sub_filter # statements don't work proxy_set_header Accept-Encoding ""; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # proxy_set_header X-Forwarded-Proto https; # I found Cake was issuing a redirect in code # $this->redirect([ # 'action' => 'pallet_print', 'marg' # ]); # and the Location: headers pointed to the # wrong place a CakePHP redirect would emit something like # Location: http://localhost/Labels/pallet_print/marg # The following # proxy_redirect statement would change it from the # which was correct! # note the $http_host variable this is the name and port the # client sees in the browser address bar # # when using docker containers # the server process (nginx, apache) can be running # on a default port e.g. 80 and then be # published to the outside world as a fixed or semi-random # non-standard port my nginx container published itself # in the thirty thousands 32840 $http_host captures what the client # sees. proxy_redirect http://localhost/ http://$http_host/tgn/; } location / { # without doing the sub_filters requests will hit this # block and miss the backend entirely # This redirect shunts anything short of /tgn/ to /tgn/ # you may not want this behavior return 301 $scheme://$http_host/tgn/; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} } |
0 Comments