bcosca / fatfree

A powerful yet easy-to-use PHP micro-framework designed to help you build dynamic and robust Web applications - fast!
2.66k stars 446 forks source link

Dockerised fatfree redirects broken #1275

Closed MarkBFamFour closed 10 months ago

MarkBFamFour commented 10 months ago

I have a Fat Free Framework 3.8 application running in a docker setup via php-fpm and nginx containers and then exposing the nginx port on the docker host. All is fine except when I use a redirect. It seems like F3 is trying to use the container hostname to dynamically build the redirect URL and it is screwing up the webapp because it is using the container hostname, not the host's where the user is browsing to.

For example:

http://192.168.1.2/reports should redirect to: http://192.168.1.2/login if no user login is detected before rendering. 192.168.1.2 is the host docker server IP

Instead it redirects to: http://9de4ghb543/login which fails as the user's web client has no knowledge of that hostname

Can I hard-code or set via a hive variable the hostname anywhere? I see there is a HOST system variable but it is currently Read-only?

Would using a nginx proxy to the docker host work better maybe if so how can I determine or setup the hostname during the docker-compose?

docker-compose.yml

version: '3.1'
services:
    webserver:
        image: 'nginx:alpine'
        working_dir: /application
        volumes:
            - '/var/www/my-app:/application'
            - './phpdocker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf'
        ports:
            - '4000:80'

    php-fpm:
        build: phpdocker/php-fpm
        working_dir: /application
        volumes:
            - '/var/www/my-app:/application'
            - './phpdocker/php-fpm/php-ini-overrides.ini:/etc/php/7.4/fpm/conf.d/99-overrides.ini'

nginx.conf

server {
    listen 80 default;

    client_max_body_size 58M;

    access_log /var/log/nginx/access.log;

    #/application/src is my f3 root
    root /application/src/public_html;
    index index.php;

    # try to serve file directly, fallback to index.php
    location / {
        try_files $uri /index.php$is_args$args;
    }

    if (!-e $request_filename) {
        rewrite ^.*$ /index.php last;
    }

    location ~ \.php$ {
        fastcgi_pass php-fpm:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PHP_VALUE "error_log=/var/log/nginx/errors.log";
        fastcgi_param APP_ENV "PRODUCTION";
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        include fastcgi_params;
    }
}
MarkBFamFour commented 10 months ago

Okay, so I manage to work this through and it looks like the HOST variable in the hive is writeable.

No need to do the above, in fact you should probably avoid it.

Credit to @pauljherring

You can simply set the server_name to your desired IP/hostname and it will be fed directly into F3 with no need to alter the hive.

I ended up doing this:

docker-compose.yml

version: '3.1'
services:
    webserver:
        image: 'nginx:alpine'
        working_dir: /application
        volumes:
            - '/var/www/my-app:/application'
            - './phpdocker/nginx/nginx.conf.template:/etc/nginx/conf.d/default.conf.template'
            - './phpdocker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf'
        ports:
            - '4000:80'
        environment:
            - HTTP_PORT: '4000'
            - HOST_IP: '192.168.1.2'

    php-fpm:
        build: phpdocker/php-fpm
        working_dir: /application
        volumes:
            - '/var/www/my-app:/application'
            - './phpdocker/php-fpm/php-ini-overrides.ini:/etc/php/7.4/fpm/conf.d/99-overrides.ini'

nginx.conf.template

server {
    listen ${HTTP_PORT} default;

    server_name ${HOST_IP}

    client_max_body_size 58M;

    access_log /var/log/nginx/access.log;

    #/application/src is my f3 root
    root /application/src/public_html;
    index index.php;

    # try to serve file directly, fallback to index.php
    location / {
        try_files $uri /index.php$is_args$args;
    }

    if (!-e $request_filename) {
        rewrite ^.*$ /index.php last;
    }

    location ~ \.php$ {
        fastcgi_pass php-fpm:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PHP_VALUE "error_log=/var/log/nginx/errors.log";
        fastcgi_param APP_ENV "PRODUCTION";
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        include fastcgi_params;
    }
}

NOTE: I used an additional .conf.template file to configure nginx. The nginx official docker image will automatically use the template to build a .conf of the same name (in this case, in the container, default.conf.template builds default.conf) and substitute in any environment variables you specified in the docker-compose.yml, provided you specify them them to the template using ${ENV_VAR} format. This means you can set your template up once and change things in the docker-compose.yml per each deployment.

Using the equivalent internal and external ports means the $_SERVER["SERVER_PORT"] directive in PHP will carry the correct port number over, and in turn means, if it is not 80, then F3 automatically bu8ilds the URL with the port number and you need only set server_name and it will build the correct URL. If for some reason you can't do this, you can add the correct port to the end of the server_name e.g. server_name ${HOST_IP}:${HTTP_PORT}.

pauljherring commented 10 months ago

For reference, and visitors from the future, it defaults to using $_SERVER['SERVER_NAME']:

 pherring@li1004-82  /var/www/beerfax.com/staging/lib/fatfree/lib  ➦ 774692c  grep "HOST'=" -rin base.php -B5 -A5  
2512-                   'FALLBACK'=>$this->fallback,
2513-                   'FORMATS'=>[],
2514-                   'FRAGMENT'=>isset($uri['fragment'])?$uri['fragment']:'',
2515-                   'HALT'=>TRUE,
2516-                   'HIGHLIGHT'=>FALSE,
2517:                   'HOST'=>$_SERVER['SERVER_NAME'],
2518-                   'IP'=>$this->ip(),
2519-                   'JAR'=>$jar,
2520-                   'LANGUAGE'=>isset($headers['Accept-Language'])?
2521-                           $this->language($headers['Accept-Language']):
2522-                           $this->fallback,
MarkBFamFour commented 10 months ago

@pauljherring So in fact you could alter it using the:

server_name example.org www.example.org;

directive in the nginx.conf?

Boy I wish someone had told me that 24 hours ago!