I am in the process of moving over some sites in PHP using Slim/Twig to docker containers. In the old server a request came in slim routed the request sent back the html and then the browser made the call to get the resources CSS, images, etc. where apache took over.
Moving to Docker the httpd conf is essentially this:
ProxyPassMatch "^/(.*\.php\/(.*)?)$" "fcgi://php:9000/var/www/html/public/index.php/$2"
Still working all that out but this essentially forwards a request like http://192.168.33.20:8080/index.php/admin
to fcgi://php:9000/var/www/html/public/index.php/$2
and Slim picks off all the rest of the path so admin
and returns the correct view rendered by twig.
Issue I am running into is the resources. I have most of my css and front-end frameworks in the php application via composer. So the browser makes another call to:
/index.php/vendor/twbs/bootstrap/dist/js/bootstrap.bundle.js HTTP/1.1" 404
Getting a 404
because Slim has no idea what this path is and apache is just forwarding.
What I have looked at and the cons:
Placing the resources on Apache but this basically couple Apache and php like the server was.
Creating a container to server these files ie another non-loadbalancer Apache to server these requests. This still couples and also means I need to find a way to potentially store multiple versions in a blue green deployment.
Mount a volume to the Apache lb. Not sure why I abandoned this one. Probably version issues.
Create a route that can search for the resource. So far my favorite idea but added code complexity.
So the question I have is there a standard way of dealing with this in Docker?
My standard practice is to control resource access by framework route. An example implementation (in Lumen):
$router->get('/asset[/{path:.*}]', 'AssetController@load');
An example request and response looks like this:
GET /asset/js/app.js HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Accept: */*
Referer: http://localhost:8080/spa/example
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
HTTP/1.1 200 OK
Server: nginx
Content-Type: application/javascript
Content-Length: 21056410
Connection: keep-alive
Last-Modified: Sun, 31 Jul 63 19:34:21 +0000
Cache-Control: private, must-revalidate
Date: Wed, 31 Jul 2019 19:34:21 GMT
Accept-Ranges: bytes
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'self'; connect-src 'self'; img-src 'self'; style-src 'unsafe-inline' fonts.googleapis.com 'self'; font-src fonts.gstatic.com 'self'; script-src 'unsafe-inline' 'self' 'unsafe-eval'
Your controller (e.g., AssetController
in my earlier example), would take care to translate the URL path to the file system path (watch out for ..
and other tricks, use realpath
and compare it to your code's installation base path), set the Content-Type
and Content-Length
headers, any relevant caching headers, and then stream the file.
While that does add complexity and a certain amount of overhead, it provides the flexibility to constrain resources by business logic.
If performance is a concern, you can promote the access to the web server or shield the route behind a CDN and proxy content.