/ nginx / docs / how-to-provide-conditional-request-settings-like-timeout-or-caching


Why conditional requests are harder to implement for PHP?

By conditional requests here I mean specific settings for specific requests. For example, caching for /results or longer timeouts for /export.

Usually this should not be that hard

Just create a specific location block and add the specific settings.

For example for a plain HTML page

# Regular stuff.
location / {
    ...
}
# Long requests.
location ~ (batch.html|export.html|import.html|csv.html|excel.html|pdf.html) {
    add_header My-Long-Timeout true always;
    include snippets/long_requests.conf;
}

In the browser You go to ‘/batch.html’ and the NGINX will apply these settings and return the the file from the file system.

Where’s the catch for the PHP?

The pretty URL like /results does not match exactly with the PHP file that needs to be executed -index.php. That’s why we need to execute it manually like this:

 # First try files like CSS, JS, images else redirect to PHP.
location / {
    try_files $uri $uri/ /index.php?$args;
}

# All PHP requests.
location ~ \index.php$ {
    include snippets/fastcgi-php.conf;
}

Why this is a problem?

Now try to add conditions here. What would you do?

Before You start maybe you want to set up a test page and try this Yourself?

1) Simply just add an if block and put the required content there, right?

Like this?

# All PHP requests.
location ~ \index.php$ {
    if ($request_uri ~ (batch|results|export|import|csv|excel|pdf) ) {
        add_header My-Long-Timeout true always;
        include snippets/long_requests.conf;
    }
    include snippets/fastcgi-php.conf;
}

Sorry, this won’t work because NGINX allows only set, rewrite and return actions and nothing else. So an error nginx: [emerg] "include" directive is not allowed here will raise when you restart the NGINX.

2) Execute index.php without a redirect

Won’t work. try_files is the only way to internally, without a redirect call a file that is not the same as the URL.

What’s wrong with the redirect here?

The original pretty URL will be lost because it will be re-written with the redirect’s URL which now is index.php. Which means:

Fine then just set the URL manually

Set the URL to the pretty URL before try_files has executed the index.php like this set $request_url /pretty-url. This won’t work too because NGINX does not allow to mess with it’s internal variables By default I mean. There are some extensions that allow that.

3) Okay, do the redirect to index.php but check location from the new URI

Won’t work, because location only checks request_url which now is index.php.

4) Use alias

	location /static-text {
		alias index.php;
		add_header My-Long-Timeout true always;
        include snippets/long_requests.conf;
        include snippets/fastcgi-php.conf;
	}

Will return 404 Not Found.

5) return to @block

location ~ \index.php$ {
        internal;

		if ($request_uri ~ (static-text) ) {
			error_page 419 = @myblock;
			return 419;
		}

        include snippets/fastcgi-php.conf;
    }

	location @myblock {
		add_header My-Long-Timeout true always;
		include snippets/long_requests.conf;
		include snippets/fastcgi-php.conf;
	}

Will work but will become messy for a complex logic - lot of conditions inside one block, it is much trickier to provide simple logical operations because NGINX has strict constraints like it does not provide conditions with multiple logical operations (AND, OR, etc.).

SOLUTION

If the problem is that files does not match URL then make them match. Duplicate index.php to index_long_request.php. This will allow to divide location blocks and now you can add a specific location block with specific settings.

Example

# First try files like CSS, JS, images else redirect to PHP.
location / {
    try_files $uri $uri/ /index.php?$args;
}
location ~ \index.php$ {
    internal;
    include snippets/fastcgi-php.conf;
}

# Long requests.
location ~ (batch|export|import|csv|excel|pdf) {
    try_files $uri /index_long_request.php?$args;
}
location ~ \index_long_request.php$ {
    internal;
    include snippets/long_requests.conf;
    include snippets/fastcgi-php.conf;
}

Working conditional requests for PHP.