Allow internal redirect to URI x, but deny external request for x?

J. Lewis Muir jlmuir at
Tue Sep 3 17:26:15 UTC 2019

On 09/02, Francis Daly wrote:
> nginx does not "do" php. nginx does not care what your fastcgi server
> will do with the key/value pairs that it sends. nginx cares that the
> fastcgi server gives a valid response to the request that nginx makes.
> Typically, your fastcgi server will use the value associated with
> SCRIPT_FILENAME as "the name of the file to execute". If your fastcgi
> server fails to find / read / execute that file, it will return its own
> error indication.
> (So your "if", or the more common "try_files", is just an early-out,
> to sometimes avoid involving the fastcgi server. It may happen that the
> file is present when nginx looks for it, but is absent when the fastcgi
> server looks for it -- so that case does have to be handled anyway.)
> In this case, if $document_root is /srv/www/my-app/current/ and
> $realpath_root is /srv/www/my-app/releases/1.0.2/, and the script
> name is test.php, then with one config, nginx would send the string
> "/srv/www/my-app/current/test.php", and with the other config nginx
> would send the string "/srv/www/my-app/releases/1.0.2/test.php".
> (That is "pathname1" vs "pathname2".)

> So if "one request" involves the fastcgi server reading
> "/srv/www/my-app/current/test.php", and then reading a bunch of other
> files in the same directory -- then I guess that unfortunate timing
> could lead to it reading some files from releases/1.0.1 and some from
> releases/1.0.2. (Assuming that it opens the directory afresh each time --
> which can't be ruled out.)
Right, that's what I was trying to avoid by using $realpath_root.  I
assumed that $realpath_root was set at the beginning of the location
processing.  That way, I could be guaranteed that it would not change
for the duration of the request handling within nginx.  And since nginx
would give that value (i.e., the path with the symlinks resolved) to
the FastCGI server, the FastCGI server would be using that same path
for the whole request and wouldn't know anything about the "current"
symlink that can change at any moment.  But perhaps that's an invalid
assumption, and the path is resolved every time $realpath_root is
expanded as a variable?  I hope not, but that would be really important
to understand.

> But if "the app" involves a http request to part1.php and then a http
> request to part2.php (or: a second http request to part1.php), I don't
> think that the symlink+realpath thing will prevent those two requests
> going to different release versions.

Hmm, good point.

I'm not sure how to do a seamless web app update deploy, then.  Maybe
it's not possible without additional constraints.

I'm assuming the app is hosted on a single nginx server.  Although, I'd
be curious how this is typically solved for a multiple-server case as
well (e.g., a load balancer with multiple identical instances of the web
app running on two or more servers).  The idea is to have no downtime.

I suppose I could encode the app version in the URI (e.g.,
/my-app/1.0.2) or in the request header.  I've seen REST APIs versioned
in the URI or in the request header, but I'm not sure web apps do that.

Or I could try to ensure that my web app updates are *always* backward
compatible if they are to keep using the same URI.  I could do that for
any web apps I write, but I can't control that for any that I don't
write and am just deploying.

Another idea I previously toyed with was to deploy the web
app in a directory structure similar to the symlink+realpath
approach but without the symlink; the path to the app root is
versioned.  In the nginx config, use the versioned path to the
app root (e.g., /srv/www/my-app/releases/1.0.2).  To deploy a new
version of the app, install to a new versioned app root (e.g.,
/srv/www/my-app/releases/1.0.3), change the app root in the nginx
config, and cause nginx to reload the config.  Note that I'm
intentionally keeping the file system path versioned so that the path
changes when a new version is deployed to avoid the need to flush any
caching that might be going on at the FastCGI server or elsewhere.

Will there be downtime for a split second when the config is reloaded?
What will happen?  Will nginx refuse connections?  Will it accept
connections but just not respond while it's reloading the config?

But this approach has the same issue that you pointed out for the
symlink+realpath approach in that I don't see a way to prevent the case
where the app involves an HTTP request to part1.php that goes to one
release and then an HTTP request to part2.php that goes to the updated
release if the deploy happens at just the right (wrong) time.

What's the best strategy for deploying a new version of an app, then?
Expect that all app updates are backward compatible?  Expect that the
app passes around a version identifier with each request that the app
will use to detect when the version has changed and force the user to
log in again or something?  Version the URI?



More information about the nginx mailing list