Nginx, try_files & multiple folders with front controllers - php

I can't wrap my head around Nginxs' try_files. Recently I installed Nginx in favour of Apache on my development machine. How I used to work was like this: I'd bootstrap a project in a subfolder of my http directory. If this involved laravel, wordpress, codeigniter or any other framework that's using a front controller to make URLs more readable, I'd add a .htaccess in that directory to rewrite all non-existing paths to index.php.
Supposedly, this is very simple on Nginx: try_files $uri $uri/ /index.php?$query_string and it should work. However, all this does is redirect everything to the index.php in the $document_root. Example:
File structure:
http/clients/cms/public/index.php front controller
http/clients/cms/public/some/application/url request uri with the parameters some, application and url.
http/clients/cms/public/images/image.png a static file.
http/clients/blog/index.php another front controller
http/clients/blog/wp-content/image.png another static file
http/clients/blog/some-article-title a 'pretty url' that should be directed to 4
Now, hen a request is made to 2, try_files detects that /some, /some/application and /some/application/url don't exist. It will now redirect to /index.php. I would expect that it would redirect to index.php in the public/ directory, but it doesn't. Instead, it redirects to the index.php in the $document_root (which happens to contain a phpinfo(); call, so it's easy to see that it goes wrong..).
A solution would be to create multiple location blocks in my Nginx config, but I'm not liking that. It would add a significant amount of extra work to my bootstrapping process.
Is there a way to use try_files in the directories so that it works exactly like the apache alternative?
My config file:
server {
listen 80;
server_name localhost,imac.local;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
rewrite_log on;
root /Volumes/Storage/Fabian/Dropbox/Sites;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
#echo $request_filename;
#break;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
#fastcgi_split_path_info ^(.+\.php)(.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
thanks!

Related

How to route request url subfolder paths to specific php pages using nginx and php-fpm

I'm using a local nginx server for the first time to set up a website i'm building and i'm having trouble setting up the nginx config to handle url requests the way I want. My website serves multiple php pages as the user navigates through the website. When developing the site initially using a local php server, I used GET requests with window.location.href changes for site navigation. For example:
http://localhost:8000/shop.php?filter=all&sort=id_asc&page=3
However, since its going to be an ecommerce website for a small business, I wanted to handle the URLs in a cleaner and more professional manner.
My site structure looks something like this:
Website:
->index.php
->shop.php
->about.php
->product-page.php
->/css/
->/javascript/
->/php/
I want to configure nginx to route url paths in the following way
www.mywebsite.com -> routes to index.php
www.mywebsite.com/shop -> routes to shop.php
www.mywebsite.com/shop/anything -> routes to shop.php
www.mywebsite.com/about -> routes to about.php
www.mywebsite.com/product -> routes to product-page.php
www.mywebsite.com/product/anything -> routes to product-page.php
I've tried numerous suggestions over a couple of days before asking here but everything failed due to one reason or another, 404s, 500 internal errors, and redirect loops. I'm hoping to gain some inside here while I move onto other aspects of the site, so as to stop beating my head against the wall. Here is the state of my nginx conf at this moment:
server {
listen 80 ;
listen [::]:80 ;
server_name localhost;
root /var/www/html/reagansrockshop;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location = /shop {
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_index shop.php;
try_files $uri /shop.php;
}
location /shop/ {
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
try_files $uri /shop.php;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
How could I go about solving this? And if there is a better standard in structuring a website and its URLS please let me know. This is my first website and first time using nginx - so i'm a little naive on best practices.
If you need a certain php script to be responsible for a whole path, you need a config like this:
root /var/www/html/reagansrockshop; # root directive is necessary to define where '/' is
location /shop/ { # this means "all URLs starting with '/shop/' "
index /shop.php; # be careful with path to the file here
}
Although I would rather recommend a more traditional and cleaner project structure.
In your project root create two directories: shop and product. Move shop.php and product-page.php into designated folder and rename both to index.php. Your nginx config for this structure will be like this:
server {
listen 80 ;
listen [::]:80 ;
server_name localhost;
root /var/www/html/reagansrockshop;
index index.php index.html;
location / {
index index.php;
try_files $uri $uri/ =404;
}
location /shop/ {
try_files $uri $uri/ /shop/index.php?$args;
}
location /product/ {
try_files $uri $uri/ /product/index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

Nginx can not read .php routes of laravel who not exist

I have created some Laravel routes with they have ".php" extention, such as
Route::get('/api/send.php', function(){
echo 'Hi There';
});
But when I open the route it shows 404 error in Nginx server... That is Nginx configurations
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/laravel/public;
# Add index.php to the list if you are using PHP
index index.php index.html index.htm index.nginx-debian.html;
server_name xxx.xxx.xxx.xxx;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
What is the problem? I have used the same code it worked before..
Thanks
SOLVED
It solved by changing
try_files $fastcgi_script_name =404;
in /etc/nginx/snippets/fastcgi-php.conf to
try_files $fastcgi_script_name /index.php?$query_string;
This is a very common problem with nginx configured to serve PHP applications, especially Laravel. You can reproduce it on sites like laravel.com, e.g: laravel.com/example.php.
The default configuration (what you're probably using in snippets/fastcgi-php.conf) is this:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
And fastcgi_script_name is...
request URI or, if a URI ends with a slash, request URI with an index file name configured by the fastcgi_index directive appended to it. This variable can be used to set the SCRIPT_FILENAME and PATH_TRANSLATED parameters that determine the script name in PHP. For example, for the “/info/” request with the following directives
That means, when a request URI contains .php it is treated as if it is a request for a PHP file, and if that PHP file doesn't exist an error is returned by nginx -- it never reaches your application.
The solution is to force fastcgi_script_name to always equal your application's entry point, in this case that's index.php. You can either edit snippets/fastcgi-php.conf or add it into your location block like this:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
}
Your application will now receive every request, including those that have .php in the path.

Laravel and Nginx static files

So I am having a problem with getting Nginx to serve static files for my Laravel app. I can see in chrome's dev tools that the requests are being made to the path, where those files should be (http://mywebsite.com/public/css/style.css). But they are not being loaded at all. I've tried getting it to work in a lot of ways, but it just doesn't seem to do the job. Could anyone help me with that? Cheers!
server {
listen 80;
server_name mydomainname.com;
keepalive_timeout 70;
root /var/www/html/portfolio/public;
index index.php index.html index.htm index.nginx-debian.html;
autoindex on;
charset utf-8;
location / {
root /var/www/html/portfolio/public;
index index.php index.html index.htm;
try_files $uri $uri/ /index.php$is_args$args;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
access_log off;
error_log /var/log/nginx/myapp-error.log error;
sendfile on;
client_max_body_size 100m;
rewrite ^ /index.php last;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_intercept_errors off;
fastcgi_buffer_size 16k;
fastcgi_buffers 4 16k;
}
location ~ /\.ht {
deny all;
}
}
Basically, there's a lot of files in /public directory that are not loading, but should be. Like for example css, js, html files for angular templates etc...
As Kyslik hinted, this could be a permission problem.
I had a similar problem, running Laravel on Homestead on Windows 10. In my case, I could access the files from public\css directory, but not from public\css\vendor. I realized that I created the vendor directory from Windows, and as a result, the files in it were not accessible by ngingx.
To fix the issue I deleted the vendor directory and recreated it from within the vagrant box.
The urls don't need to include the /public part in the path. /public is the root for your web assets. So your url should be:
http://mywebsite.com/css/style.css
All you have to do is understanding the server is a different pc entirely and resolving path range for hosting service all around the global.
Best practice is to make sure your assets are only one step deeper into your application
Eg.
<link href="folder/bootstrap/css/bootstrap.min.css" /> wrong
<link href="bootstrap/bootstrap.min.css" /> correct
The server will not stress scanning directory. and the server will only go one step deeper.
AWS Ec2 Instance Ubuntu x Laravel Application.

laravel forge set up with angularjs

I have just got forge up and running and serving my site and it works great. The frontend of my site is pure angular.
I have abstracted all of the angular aspects out of the laravel app into its own directoy on my local machine and have mapped this directory to say code/app/angular on the forge vm
the laravel app is mapped to code/app/laravel
lets say I have named the site awesome.app in the Homestead.yaml file and I have mapped that in my hosts file. what I want / need is to be able to hit awesome.app and have the angular app returned and then for any calls made by the angular app to the laravel backend to be made through awesome.app/users/1 and for the laravel app to pick that up and return the correct response. Is this possible?
so basically awesome.app should return the static files and then the laravel app should be listening for requests on awesome.app/
edit: I can create two sites on the vm one for the laravel app and one for the angular and then set up angular so its default base url is the laravel site but this is not ideal as it would mean removing that setting from angular before pushing to production
edit1: I imagine that this is going to require setting up nginx to reply with the contents of the angular folder when the root domain (awesome.app) is requested and to send the rest of the requests (awesome.app/ or awesome.app/api/)to the laravel folder. I do not know how to configure nginx at all as I was using apache until switching to homestead today.
The ultimate goal being to keep the angular and laravel codebases seperate so that I can push just the angular app to s3 and just the laravel app to ec2 / elasticbeanstalk
edit2:
Thanks fideloper I finally got a chance to try out your suggestion and I think I got most of the way there. the angular app from /home/vagrant/code/awesome/angular/build is being served correctly but the requests to the /api route are returning no input file specified. I dug around and I dont think its a permissions issue as I can access the laravel app with a vanilla homestead setup so I imagine it is something wrong with the config
the laravel app is in /home/vagrant/code/awesome/laravel with the index.php being in /public as normal. as a result I changed your suggestion slightly by moving the root to the location blocks as the roots are different for / and /api. I also tried keeping the root in the server block and adding an alias in the /api/ location block instead but this also returned no input file specified
maybe you can see where I am going wrong?
server {
listen 80;
server_name awesome.app;
index index.html index.htm;
charset utf-8;
location / {
root /home/vagrant/Code/awesome/angular/build;
try_files $uri $uri/ =404;
}
location /api/ {
root /home/vagrant/Code/awesome/laravel/public;
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \/api\/index\.php$ {
root /home/vagrant/Code/awesome/laravel/public;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
#This specifically sets so the /api/ portion
#of the URL is not included
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param ENV production;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
access_log off;
error_log /var/log/nginx/awesome.app-error.log error;
error_page 404 /index.php;
sendfile off;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
edit3: if I remove that second location ~ .php$ location block (which I am guessing I should) I get an actual nginx 404 html page not just a 404 with no input file specified
edit4: below config works
server {
listen 80;
server_name awesome.app;
root /home/vagrant/Code/awesome/laravel/public;
index index.html index.htm index.php;
charset utf-8;
location /api/ {
try_files $uri $uri/index.php?$query_string;
}
location / {
root /home/vagrant/Code/awesome/angular/build;
try_files $uri $uri/ index.html;
}
location ~ \/api\/index\.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
#This specifically sets so the /api/ portion
#of the URL is not included
#fastcgi_param SCRIPT_FILENAME $document_root/index.php;
#fastcgi_param PATH_INFO $fastcgi_path_info;
#fastcgi_param ENV production;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
access_log off;
error_log /var/log/nginx/awesome-error.log error;
error_page 404 /index.php;
sendfile off;
location ~ \.php$ {
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
You're right on your assumption on the Nginx configuration. I would do something like this on the nginx configuration (change names or file paths as needed for Homestead. I think it puts code in /home/vagrant/www instead of /var/www for example).
One Issue
I see you are differentiating at first with the url http://awesome.app vs http://awesome.app/ - however those are effectively the same URI. I would go with your second idea of http://awesome.app/ being static and http://awesome.app/api being your Laravel app.
You have two avenues of attack (well, 2 that I'll talk to, I'm sure there' more) for that:
Have Laravel serve the HTML for the home page (what you would otherwise have in an index.html page). Then create the /api route within Laravel. This lets you use a stock Nginx configuration. The story ends there, this is the easier way.
Configure the /api/ URL in Nginx to talk to PHP, while leaving any other URL to only serve static assets. A natural consequence of this is a bit more work in in your Nginx configuration.
Here's what that might look like:
server {
listen 80;
server_name awesome.app;
root /var/www/awesome.app/public;
# Not listing index.php file here on purpose
index index.html index.htm;
charset utf-8;
access_log /var/log/nginx/awesome.app.log;
error_log /var/log/nginx/awesome.app-error.log error;
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
location / {
try_files $uri $uri/ =404;
}
# Make index.php in /api url unnecessary
location /api {
try_files $uri /api/index.php$is_args$args;
}
# Only parse PHP for /api/index.php
location ~ \/api\/index\.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
# This specifically sets so the /api/ portion
# of the URL is not included
fastcgi_param SCRIPT_FILENAME $document_root/index.php
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param ENV production;
}
}
As a result, only the /api URL will be parsed by PHP.
Now, in this setup, your laravel application might get called with the url /api/index.php, which should just work still when using a base route Route::get('/', ...);.
Personally I'd keep this simpler and not do this funky Nginx setup, since it could cause more issues than it's worth. However it all depends on your setup.

Nginx with CakePHP in subfolder not serving static files (CSS, JS, IMG)

I am trying to setup a Nginx server configuration to serve a CakePHP installation from and to a subfolder.
URL: https://sub.domain.com/cakefolder
Folder on system: /var/www/domain/sub/cakefolder
So i am using a sub folder for both the URL as well as on the system. Now it took me a while to figure the following config out with which requests are properly handled by CakePHP. This means it's correctly bootstraping and handling controllers.
What doesn't work however, is serving static files from the webroot directory (e.g. *.css files) as those are all interpreted as CakePHP controllers leading to a CssController could not be found. error.
My site.conf:
server {
listen *:80;
listen *:443 ssl;
server_name sub.domain.com;
ssl_certificate ./ssl/domain.crt;
ssl_certificate_key ./ssl/domain.key;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
if ($ssl_protocol = "") {
rewrite ^ https://$server_name$request_uri? permanent;
}
root /var/www/domain/sub/cakefolder/;
autoindex off;
index index.php;
location /cakefolder {
root /var/www/domain/sub/cakefolder/app/webroot/;
try_files $uri $uri/ /cakefolder/index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
How do I stop Nginx from routing existing static files through the FastCGI PHP interpreter?
Based on https://stackoverflow.com/a/22550332/671047 I already tried replacing my location /cakefolder { ... } with
location ~ /cakefolder/(.*) {
try_files /cakefolder/$1 /cakefolder/$1/ /cakefolder/index.php?$args;
}
but this leads to a redirection loop causing a HTTP 500 error.
Solution (thanks Pete!):
I found the following additional directive to be working for this specific setup. This might not be the most elegant solution but who cares, glad it's working for now.
location ~* /cakefolder/(.*)\.(css|js|ico|gif|png|jpg|jpeg)$ {
root /var/www/domain/sub/cakefolder/app/webroot/;
try_files /$1.$2 =404;
}
you could catch it early:
location ~* \.(css|js|ico)$ {
try_files $uri =404;
}
i have a similar setup and that worked for me when i experienced the same thing (just not cake.) I won't lie, i never understood why the try_files w/redirect always failed on existing static files, where as throwing a try_files like ^above finds the file np. ideas on that? (perhaps today is a source-reading day)

Categories