WiR3D WiR3D - 2 months ago 12
PHP Question

nginx add_header on specific URI to PHP app with front controller

I have a pretty standard setup with a symfony2-like app with a front controller, running on nginx 1.10 and Centos7. It all works as expected, blocks where expected etc.

server {
listen 80;

root /opt/my/code/web;
index app.php;
charset utf-8;

location / {
try_files $uri $uri/ /app.php$is_args$args;
}

# pass the PHP scripts to php5-fpm
location ~ ^/app\.php(/|$) {

# problem here
location ~ ^/recording {
add_header Content-Type audio/x-wav;
}

fastcgi_split_path_info ^(.+?\.php)(/?.*)$;
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index app.php;
include /etc/nginx/fastcgi_params;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

# Prevents URIs that include the front controller. This will 404:
internal;
}

# return 404 for all other php files not matching the front controller
location ~ \.php$ {
return 404;
}
}


I have a few issues but the main one is that I want special handling for a URI matching
/recording
but it still has to go through the front controller. (This is not debatable, it HAS to go through the front controller and modify a response header if the URI matches
/recording
)

Since
try_files
redirects to
location ~ ^/app\.php(/|$)
nginx's
$uri
parameter used for location matching gets updated to
/app.php
, so any nested locations won't work.

I cant use
add_header
outside of the front controller block because any
add_header
directives get dropped on an internal redirect.

Obviously I can't use
location if
with
add_header
either.

This is easy in apache, but the only remote solution I have found uses a third party lua module and the installation docs are a bit thin on that and the thought of compiling that in from source on centos is giving me heart palpitations.

Answer

If internal redirect bother us, lets remove internal redirect :) You can solve it easy with fastcgi config duplication

server {
    listen 80;

    root /opt/my/code/web;
    index app.php;
    charset utf-8;

    location / {
        try_files $uri $uri/ /app.php$is_args$args;
    }

    location ~ ^/recording {
        add_header Content-Type audio/x-wav;
        fastcgi_pass unix:/var/run/php-fpm.sock;
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_NAME /app.php;
        fastcgi_param SCRIPT_FILENAME $document_root/app.php;
    }    

    # pass the PHP scripts to php5-fpm
    location ~ ^/app\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/?.*)$;
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_index app.php;
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        # Prevents URIs that include the front controller. This will 404:
        internal;
    }

    # return 404 for all other php files not matching the front controller
    location ~ \.php$ {
        return 404;
    }
}

Second solution works only if you know content type of all other requests. We can use variables. (btw, I don't suggest this solution because harder to support and not cute :))

server {
    listen 80;

    root /opt/my/code/web;
    index app.php;
    charset utf-8;

    location / {
        try_files $uri $uri/ /app.php$is_args$args;
        set $ct "text/html";
    }

    location ~ ^/recording {
        try_files $uri $uri/ /app.php$is_args$args;
        set $ct "audio/x-wav";
    }    

    # pass the PHP scripts to php5-fpm
    location ~ ^/app\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/?.*)$;
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_index app.php;
        include /etc/nginx/fastcgi_params;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        add_header "Content-Type $ct;

        # Prevents URIs that include the front controller. This will 404:
        internal;
    }

    # return 404 for all other php files not matching the front controller
    location ~ \.php$ {
        return 404;
    }
}
Comments