Nginx caching symlinks

Solution 1:

Embedded Variables, $realpath_root: an absolute pathname corresponding to the root or alias directive’s value for the current request, with all symbolic links resolved to real paths

The solution of using $realpath_root instead of $document_root is copy-pasted all around the Q/A sites and forums; it is actually hard to avoid finding it.Yet, I've only seen it well explained once by Rasmus Lerdorf. It's worth sharing as it describes why it works and when it should be used.

So, when you deploy via something like Capistrano which does a symlink swap on the document root, you want all new requests to get the new files, but you don't want to screw over requests that are currently executing as the deploy is happening. What you really need to create a robust deploy environment is to have your web server be in charge of this. The web server is the piece of the stack that understands when a new request is starting. The opcode cache is too deep in the stack to know or care about that.

With nginx this is quite simple. Just add this to your config:

fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;

This tells nginx to realpath resolve the docroot symlink meaning that as far as your PHP application knows, the target of the symlink if the real document_root. Now, once a request starts, nginx will resolve the symlink as it stands at that point and for the duration of the request it will use the same docroot directory, even if the symlink switch happening mid-request. This entirely eliminates the symptoms described here and it is the correct approach. This isn't something that can be solved at the opcache level.

Kanishk Dudeja had problems with this and added a useful notice: make sure these changes will actually be in final configuration, i.e. after include fastcgi_params; which otherwise overrides them.

Solution 2:

From https://unix.stackexchange.com/questions/157022/make-nginx-follow-symlinks, it seems you may be able to work around the issue by changing

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

to

fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;

(i.e. changing the path from $document_root to $realpath_root).

I don't have access to an nginx server at present to confirm this (my home server is currently undergoing a rebuild), but the solution seems to be collaborated by https://medium.com/@kanishkdudeja/truly-atomic-deployments-with-nginx-and-php-fpm-aed8a8ac1cd9.