diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0d207b..6528db4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,7 +110,34 @@ jobs: - run: docker logs $(docker ps -qn1) if: ${{ always() }} - nginx-webserver: + nginx-reverse-proxy: + name: nginx (${{ matrix.config.name }}) + runs-on: ubuntu-22.04 + strategy: + matrix: + config: + - name: reverse proxy with files + path: nginx-reverse-proxy-public.conf + - name: minimal reverse proxy + path: nginx-reverse-proxy-minimal.conf + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + - run: composer install -d tests/integration/ + - run: docker build -f tests/integration/Dockerfile-basics tests/integration/ + - run: docker run -d -p 8080:8080 -v "$PWD/composer.json":/app/composer.json $(docker images -q | head -n1) + - run: docker run -d --net=host -v "$PWD/tests/integration/":/home/framework-x/ -v "$PWD"/tests/integration/${{ matrix.config.path }}:/etc/nginx/conf.d/default.conf nginx:stable-alpine + - run: bash tests/await.sh http://localhost + - run: bash tests/integration.bash http://localhost + - run: docker stop $(docker ps -qn2) + - run: docker logs $(docker ps -qn1) + if: ${{ always() }} + - run: docker logs $(docker ps -qn2 | tail -n1) + if: ${{ always() }} + + nginx-fpm: name: nginx + PHP-FPM (PHP ${{ matrix.php }}) runs-on: ubuntu-22.04 strategy: diff --git a/docs/best-practices/deployment.md b/docs/best-practices/deployment.md index 8008e27..62ca3a1 100644 --- a/docs/best-practices/deployment.md +++ b/docs/best-practices/deployment.md @@ -70,6 +70,12 @@ server { try_files $uri $uri/ /index.php$is_args$args; } + # Optional: handle Apache config with Framework X if it exists in `public/` + error_page 403 = /index.php; + location ~ \.htaccess$ { + deny all; + } + location ~ \.php$ { fastcgi_pass localhost:9000; fastcgi_split_path_info ^(.+\.php)(/.+)$; @@ -186,6 +192,9 @@ RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule .* index.php +# Optional: handle `.htaccess` with Framework X instead of `403 Forbidden` +ErrorDocument 403 /%{REQUEST_URI}/../index.php + # This adds support for authorization header SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0 ``` @@ -412,20 +421,44 @@ all you need to do is to point the nginx' [`root`](http://nginx.org/en/docs/http to instruct nginx to process any dynamic requests through X. This can be achieved by using an nginx configuration with the following contents: -``` -server { - root /home/alice/projects/acme/public; - index index.php index.html; +=== "nginx.conf (reverse proxy with static files)" - location / { - try_files $uri $uri/ @x; + ``` + server { + # Serve static files from `public/`, proxy dynamic requests to Framework X + location / { + location ~* \.php$ { + try_files /dev/null @x; + } + root /home/alice/projects/acme/public; + try_files $uri @x; + } + + location @x { + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header Connection ""; + } + + # Optional: handle Apache config with Framework X if it exists in `public/` + location ~ \.htaccess$ { + try_files /dev/null @x; + } } + ``` - location @x { - proxy_pass http://localhost:8080; +=== "nginx.conf (minimal reverse proxy)" + + ``` + server { + # Proxy all requests to Framework X + location / { + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header Connection ""; + } } -} -``` + ``` > ℹ️ **New to nginx?** > diff --git a/tests/integration.bash b/tests/integration.bash index 2f26535..ca8b1d9 100755 --- a/tests/integration.bash +++ b/tests/integration.bash @@ -23,9 +23,13 @@ skipifnot() { } out=$(curl -v $base/ 2>&1); match "HTTP/.* 200" && match -iP "Content-Type: text/plain; charset=utf-8[\r\n]" -out=$(curl -v $base/invalid 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" -out=$(curl -v $base// 2>&1); match "HTTP/.* 404" -out=$(curl -v $base/ 2>&1 -X POST); match "HTTP/.* 405" +out=$(curl -v $base/ 2>&1 -X POST); match "HTTP/.* 405" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" + +out=$(curl -v $base/unknown 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" +out=$(curl -v $base/index.php 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" +out=$(curl -v $base/.htaccess 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" +out=$(curl -v $base// 2>&1); match "HTTP/.* 404" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" + out=$(curl -v $base/error 2>&1); match "HTTP/.* 500" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" && match "Unable to load error" out=$(curl -v $base/error/null 2>&1); match "HTTP/.* 500" && match -iP "Content-Type: text/html; charset=utf-8[\r\n]" diff --git a/tests/integration/nginx-fpm.conf b/tests/integration/nginx-fpm.conf index 529a1c8..57d5158 100644 --- a/tests/integration/nginx-fpm.conf +++ b/tests/integration/nginx-fpm.conf @@ -6,6 +6,12 @@ server { try_files $uri $uri/ /index.php$is_args$args; } + # Optional: handle Apache config with Framework X if it exists in `public/` + error_page 403 = /index.php; + location ~ \.htaccess$ { + deny all; + } + location ~ \.php$ { fastcgi_pass php:9000; fastcgi_split_path_info ^(.+\.php)(/.+)$; diff --git a/tests/integration/nginx-reverse-proxy-minimal.conf b/tests/integration/nginx-reverse-proxy-minimal.conf new file mode 100644 index 0000000..90f024f --- /dev/null +++ b/tests/integration/nginx-reverse-proxy-minimal.conf @@ -0,0 +1,8 @@ +server { + # Proxy all requests to Framework X + location / { + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header Connection ""; + } +} diff --git a/tests/integration/nginx-reverse-proxy-public.conf b/tests/integration/nginx-reverse-proxy-public.conf new file mode 100644 index 0000000..0b53e39 --- /dev/null +++ b/tests/integration/nginx-reverse-proxy-public.conf @@ -0,0 +1,21 @@ +server { + # Serve static files from `public/`, proxy dynamic requests to Framework X + location / { + location ~* \.php$ { + try_files /dev/null @x; + } + root /home/framework-x/public; + try_files $uri @x; + } + + location @x { + proxy_pass http://localhost:8080; + proxy_set_header Host $host; + proxy_set_header Connection ""; + } + + # Optional: handle Apache config with Framework X if it exists in `public/` + location ~ \.htaccess$ { + try_files /dev/null @x; + } +} diff --git a/tests/integration/public/.htaccess b/tests/integration/public/.htaccess index 971661e..5075818 100644 --- a/tests/integration/public/.htaccess +++ b/tests/integration/public/.htaccess @@ -4,5 +4,8 @@ RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule .* index.php +# Optional: handle `.htaccess` with Framework X instead of `403 Forbidden` +ErrorDocument 403 /%{REQUEST_URI}/../index.php + # This adds support for authorization header SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0